基于MCP协议为Cursor AI构建本地对话记忆库的实践指南
在AI辅助编程领域,上下文管理是提升开发效率的核心挑战。Model Context Protocol(MCP)作为一种标准化协议,定义了AI模型与外部数据源之间的通信规范,其价值在于实现工具能力的模块化与可复用。通过构建MCP服务器,开发者能够将本地数据智能地注入AI对话流程,从而突破单次对话的上下文限制。在工程实践中,结合SQLite进行结构化存储与向量化检索技术,可以构建高效的项目知识库。本文
1. 项目概述:一个为 Cursor 编辑器注入记忆的 MCP 服务器
如果你和我一样,深度依赖 Cursor 作为主力代码编辑器,并且每天都在和它的 AI 伙伴(无论是 Claude 还是 GPT)进行高频对话,那你一定遇到过这个痛点: 对话历史是“健忘”的 。每次开启一个新聊天,或者项目重启,AI 助手就像一张白纸,完全不记得我们之前讨论过的项目架构、刚刚定下的命名规范,甚至是几分钟前才解决的某个棘手的 Bug。这意味着我们不得不反复粘贴上下文、重新解释背景,效率大打折扣。
S2thend/cursor-history-mcp 这个项目,就是为了根治这个“健忘症”而生的。它是一个 MCP(Model Context Protocol)服务器 ,专门为 Cursor 编辑器设计。简单来说,它就像给你的 Cursor AI 助手装上了一块“外置硬盘”,能够自动、智能地保存和管理你与 AI 的所有对话历史、项目上下文,并在你需要时,精准地将相关记忆“喂”给 AI,让它瞬间“想起”一切。
这个工具的核心价值在于,它将一次性的、孤立的 AI 对话,转变成了一个持续积累、可被检索的 项目知识库 。对于需要长期维护复杂项目、或与 AI 进行深度结对编程的开发者而言,这不仅仅是效率工具,更是工作流的一次质变。接下来,我将带你深入拆解它的设计思路、实现细节,并分享我将其集成到日常开发中的实战经验与避坑指南。
2. 核心设计思路与 MCP 协议解析
2.1 为什么是 MCP?协议选型的深层考量
在深入代码之前,我们必须先理解 MCP(Model Context Protocol) 是什么,以及为什么它是解决 Cursor 上下文记忆问题的最佳载体。
MCP 是由 Anthropic 提出的一种开放协议,其核心目标是 标准化 AI 模型与外部工具、数据源之间的通信方式 。你可以把它想象成 AI 世界的“USB 协议”或“HTTP 协议”。在 MCP 架构下,AI 模型(如 Claude)是“客户端”,而各种提供特定能力(如读取文件、搜索网络、查询数据库)的服务则是“服务器”。客户端通过标准的 MCP 请求,可以调用服务器提供的各种“工具”(Tools)和“资源”(Resources)。
对于 cursor-history-mcp 这个项目,选择基于 MCP 构建,是经过深思熟虑的:
- 非侵入性与未来兼容性 :MCP 是 Cursor(以及未来其他支持 MCP 的编辑器/IDE)官方支持和倡导的扩展方式。通过实现一个 MCP 服务器,我们无需修改 Cursor 编辑器或 AI 模型本身的任何代码。这保证了工具的稳定性,也意味着只要 Cursor 持续支持 MCP,这个工具就能一直工作下去。
- 能力标准化与聚焦 :MCP 协议明确定义了服务器能提供什么(工具和资源)。
cursor-history-mcp的核心能力非常聚焦:提供“历史对话”这个资源。AI 模型通过标准的 MCPread_resource调用,就能获取到格式化后的历史记录。这种设计清晰地将“记忆存储与检索”这个复杂功能,封装成了一个标准的、可被 AI 理解的服务。 - 灵活的上下文管理 :MCP 允许服务器动态地告诉客户端“我现在有哪些资源可用”。这意味着
cursor-history-mcp可以根据当前项目、时间或用户指令,智能地决定提供哪一段历史、以何种摘要形式提供,从而在有限的上下文窗口内,实现价值最大化。
注意 :虽然 MCP 由 Anthropic 提出,但它是一个开放协议。这意味着
cursor-history-mcp理论上也能服务于其他实现了 MCP 客户端的 AI 工具,这为项目的长期价值提供了更多可能性。
2.2 项目架构与数据流设计
理解了 MCP 的基础,我们来看 cursor-history-mcp 是如何具体工作的。它的架构可以概括为“ 监听-存储-处理-提供 ”四个环节。
- 监听环节 :服务器需要实时获取 Cursor 中 AI 对话的内容。这通常通过监听 Cursor 产生的特定日志文件、或利用 Cursor 可能提供的 API(如果未来开放)来实现。项目需要解决如何可靠、低延迟地捕获到完整的对话流(包括用户提问和 AI 回复)。
- 存储环节 :捕获到的原始对话数据需要被持久化。这里涉及到存储选型:
- 本地文件存储(如 SQLite) :优点是零依赖、部署简单、速度快。
cursor-history-mcp很可能采用这种方式,将历史数据以结构化的形式(例如按项目、会话、时间戳)保存在用户本地的一个数据库中。 - 向量数据库(可选进阶) :如果未来要实现基于语义的智能检索(例如“帮我找到上次讨论用户登录模块的对话”),那么引入像
ChromaDB或LanceDB这样的轻量级向量数据库,将对话内容转换为向量存储,会是更强大的方案。
- 本地文件存储(如 SQLite) :优点是零依赖、部署简单、速度快。
- 处理环节 :原始对话日志可能是杂乱的文本。服务器需要对其进行清洗、结构化,并可能生成摘要。例如,将一次完整的 Q&A 识别为一个“对话轮次”,并提取关键实体(如提到的文件名、函数名、错误代码)。这一步是提升后续检索精度的关键。
- 提供环节(MCP 服务器核心) :当 Cursor 的 AI 模型需要上下文时,它会通过 MCP 向本服务器发起请求。服务器根据请求参数(可能隐含了当前文件路径、项目信息),从存储中检索出最相关的历史对话片段,按照 MCP 资源的标准格式(通常是包含
text或json内容的响应体)返回。
整个数据流形成了一个闭环:AI 对话产生历史,历史被保存和加工,加工后的历史在需要时又被注入到新的 AI 对话中,从而提升新对话的质量。
3. 核心功能拆解与实现要点
3.1 对话历史的捕获与解析
这是项目最基础,也最可能出问题的环节。Cursor 本身并未公开官方的实时对话流 API,因此开发者需要一些“巧劲”。
常见实现方案与取舍:
- 方案A:监听日志文件 :Cursor 在运行过程中可能会在特定目录(如
~/Library/Logs/Cursor/或%APPDATA%/Cursor/logs/)生成日志文件。这些日志可能包含对话内容。需要编写一个文件监听器(如使用 Node.js 的chokidar库),实时跟踪日志文件的追加内容,并通过正则表达式或特定标记来解析出对话。- 优点 :实现相对直接,不依赖网络。
- 缺点 :高度依赖 Cursor 的日志格式,一旦 Cursor 更新导致日志格式变化,解析器就可能失效,稳定性风险高。且日志可能包含敏感信息,需要谨慎处理。
- 方案B:模拟或拦截网络请求 :Cursor 与后端 AI 服务(如 Anthropic, OpenAI)的通信是通过网络请求完成的。理论上可以设置一个本地代理,拦截这些请求和响应来获取对话内容。
- 优点 :获取的数据结构清晰、完整。
- 缺点 :实现复杂,涉及 HTTPS 解密(可能需要安装自签名证书),对用户来说配置门槛高,且可能引发安全软件告警。
- 方案C:利用 Cursor 的扩展能力(如果存在) :持续关注 Cursor 官方是否提供插件或扩展 API。这是最理想、最稳定的方式,但目前可能还不存在。
实操心得与避坑指南:
从我尝试类似项目的经验来看, 方案A(监听日志)是目前可行性最高的起点 ,但必须做好防御性编程。
- 健壮的日志解析 :不要写死正则表达式。应该先收集不同场景下(正常对话、代码编辑、错误状态)的大量日志样本,找出对话内容的稳定模式(例如,是否以
[USER]和[ASSISTANT]这样的标记开头)。解析代码要有足够的容错性,匹配失败时记录警告而非直接崩溃。 - 处理日志轮转与切割 :日志文件可能会在达到一定大小后被轮转(重命名并新建一个)。你的监听器必须能处理
rename和create事件,确保监听不中断。 - 性能与缓冲区 :日志写入可能很快,特别是当 AI 流式输出时。避免在每次文件变化事件中都进行完整的读取和解析。应该实现一个小的去抖(debounce)机制和缓冲区,累积一小段时间内的变化再统一处理,以减少 IO 压力。
- 隐私安全 :明确告知用户该工具会读取并存储对话日志。考虑提供选项,允许用户排除某些包含敏感信息的项目或会话不被记录。存储时,是否需要对内容进行简单的脱敏处理(如自动遮盖可能的密钥模式)?
3.2 历史数据的存储与索引策略
数据捕获后,我们需要一个高效、可靠的方式来存储它。 cursor-history-mcp 作为一个本地优先的工具, SQLite 几乎是必然选择。
数据库表结构设计建议:
-- 会话表:代表一次连续的 Cursor 使用周期(可能对应一个项目窗口)
CREATE TABLE sessions (
id TEXT PRIMARY KEY, -- UUID
project_path TEXT, -- 项目根目录,用于关联历史
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 对话记录表:存储每一轮完整的 Q&A
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT,
role TEXT CHECK(role IN ('user', 'assistant', 'system')),
content TEXT, -- 完整的消息内容
tokens INTEGER, -- 估算的token数,用于上下文窗口管理
metadata TEXT, -- JSON字段,存储额外信息,如涉及的文件、语言、模型等
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
);
-- 索引表(可选,用于加速基于内容的检索)
CREATE TABLE message_embeddings (
message_id INTEGER PRIMARY KEY,
embedding BLOB, -- 存储文本的向量化表示
FOREIGN KEY (message_id) REFERENCES messages(id) ON DELETE CASCADE
);
为什么这样设计?
- 会话(Session)抽象 :将对话按“项目窗口”或“时间窗口”组织,符合用户的心理模型。当用户在新项目中问 AI 时,服务器可以优先提供同一
project_path下的历史会话,相关性最高。 - 元数据(Metadata)字段 :这是实现智能检索的关键。在解析消息时,可以运行一个简单的分析器,提取出消息中提到的文件路径(如
./src/utils/api.js)、函数名、错误码、语言类型等,存入metadata的 JSON 中。这样,后续可以根据“当前打开的文件”快速找到相关的历史讨论。 - Token 计数 :AI 模型的上下文窗口是宝贵资源。记录每条消息的大致 token 数,可以帮助服务器在响应 MCP 请求时,做出更优的决策:是提供完整的最近 10 条对话,还是提供 3 天前一次重要讨论的摘要?
进阶考量:向量索引 对于“帮我找到关于用户认证逻辑的讨论”这类语义搜索需求,仅靠关键词匹配(元数据)是不够的。这就是 message_embeddings 表的用途。你可以使用一个本地运行的轻量级嵌入模型(如 all-MiniLM-L6-v2 ,通过 sentence-transformers 库),将消息内容转换为向量。当用户提出一个语义查询时,将查询语句也转换为向量,并在数据库中进行余弦相似度搜索,找到最相关的历史对话。这一步计算量稍大,可以作为可选的高级功能。
3.3 MCP 服务器端点的实现
这是项目作为 MCP 服务器的核心。你需要实现 MCP 协议规定的几个标准端点。这里我们聚焦于最关键的 resources 和 read_resource 。
1. 声明可用的资源( resources )
当 Cursor(MCP 客户端)初始化连接时,它会调用 tools/list 或 resources/list 。你的服务器需要返回一个资源列表,告诉客户端“我这里有这些历史资源可供查询”。
// 服务器响应示例
{
"resources": [
{
"uri": "cursor-history://session/recent",
"name": "Recent conversation history",
"description": "The most recent messages from the current project session.",
"mimeType": "text/plain"
},
{
"uri": "cursor-history://project/context",
"name": "Project context summary",
"description": "A summarized context of all conversations in the current project.",
"mimeType": "application/json"
},
{
"uri": "cursor-history://search?q={query}",
"name": "Search history",
"description": "Search through all past conversations.",
"mimeType": "text/plain"
}
]
}
关键点 : uri 是资源的唯一标识符,也是客户端请求时的地址。你可以设计不同的 URI 模式来代表不同类型的查询(如最近记录、项目总结、搜索)。
2. 处理资源读取请求( read_resource )
当 AI 模型需要历史上下文时,Cursor 会向你的服务器发送一个 read_resource 请求,其中包含它想获取的 uri 。
你的服务器需要:
- 解析
uri,理解客户端的意图(是要最近记录,还是搜索某个关键词)。 - 根据
uri参数和 当前上下文 (如客户端可能在请求中附带当前文件路径,这取决于 MCP 客户端的实现,有时需要服务器从其他途径获取),从 SQLite 数据库中执行相应的查询。 - 将查询结果格式化为
text/plain或application/json内容,并返回。
// 伪代码示例:处理 read_resource
async function handleReadResource(uri, currentProjectPath) {
if (uri.startsWith('cursor-history://session/recent')) {
const recentMessages = await db.getRecentMessages(currentProjectPath, 10); // 获取当前项目最近10条
return {
contents: [{
type: 'text',
text: formatMessagesAsText(recentMessages) // 将消息数组格式化为易读的文本
}]
};
} else if (uri.startsWith('cursor-history://search')) {
const query = new URL(uri).searchParams.get('q');
const results = await db.semanticSearch(query, currentProjectPath);
return {
contents: [{
type: 'text',
text: formatSearchResults(results)
}]
};
}
// ... 处理其他 uri 模式
}
3. 上下文的动态注入 一个优秀的 cursor-history-mcp 服务器不应该被动等待调用。它可以更智能。例如,当检测到用户在新会话中打开了某个文件( main.ts ),服务器可以主动通过 MCP 的 notifications 或是在 resources/list 中动态添加一个资源,如 cursor-history://file/main.ts ,暗示 AI 客户端“这里有关于这个文件的历史讨论,你可以考虑读一下”。这需要服务器与编辑器有更深度的集成感知。
4. 部署、配置与 Cursor 集成实战
4.1 本地开发与运行环境搭建
假设项目使用 Node.js 开发(这是实现 MCP 服务器的常见选择),你需要确保环境就绪。
- 克隆项目与安装依赖 :
git clone https://github.com/S2thend/cursor-history-mcp.git cd cursor-history-mcp npm install # 或 pnpm install / yarn install - 环境变量配置 :项目根目录下可能需要一个
.env文件或配置文件(如config.json)。
关键配置解析 :# .env 示例 CURSOR_LOG_PATH=~/Library/Logs/Cursor/Cursor.log DB_PATH=./data/history.db # 是否启用向量搜索(会增加内存和CPU占用) ENABLE_SEMANTIC_SEARCH=false # 本地嵌入模型路径(如果启用) EMBEDDING_MODEL_PATH=./models/all-MiniLM-L6-v2CURSOR_LOG_PATH:这是最关键的路径,需要根据你的操作系统(macOS, Windows, Linux)和 Cursor 的安装位置准确配置。如果路径不对,服务器将“听”不到任何对话。DB_PATH:指定 SQLite 数据库存放位置。建议放在项目data目录下,便于管理。
- 启动服务器 :通常项目会提供一个启动脚本。
启动后,服务器默认会在某个本地端口(如npm start # 或用于开发的热重载模式 npm run dev3000)监听,等待 Cursor 连接。
4.2 在 Cursor 中配置 MCP 服务器
这是将你的服务器与 Cursor AI 连接起来的关键一步。Cursor 的 MCP 配置通常在一个全局或项目级的配置文件中。
-
找到 Cursor 的 MCP 配置 :配置文件可能位于:
- 全局配置 :
~/.cursor/mcp.json(macOS/Linux) 或%APPDATA%/Cursor/mcp.json(Windows)。 - 项目级配置 :在项目根目录下的
.cursor/mcp.json。 项目级配置优先级更高 ,这允许你为不同项目设置不同的历史策略。
- 全局配置 :
-
编辑 MCP 配置文件 :在配置文件中添加你的服务器信息。
// ~/.cursor/mcp.json 或 .cursor/mcp.json { "mcpServers": { "cursor-history": { "command": "node", "args": [ "/ABSOLUTE/PATH/TO/YOUR/cursor-history-mcp/build/index.js" // 必须使用绝对路径! ], "env": { "CURSOR_LOG_PATH": "/Users/yourname/Library/Logs/Cursor/Cursor.log" } } } }重要细节 :
command:启动服务器的命令。如果你的服务器是 Node.js 脚本,就是node。如果是打包成的二进制文件,则指向该二进制文件。args:传递给命令的参数,第一个通常是服务器入口文件的 绝对路径 。相对路径很可能导致 Cursor 启动失败。env:可以在这里覆盖服务器所需的环境变量,这对于在不同机器上保持配置一致性很有用。
-
重启 Cursor :修改配置后,需要完全关闭并重新打开 Cursor,以确保新的 MCP 配置被加载。
4.3 验证与调试
配置完成后,如何确认一切工作正常?
- 检查 Cursor 连接 :在 Cursor 中打开 AI 聊天面板,有时在输入框附近会有微小的提示或图标,表明已连接的 MCP 服务器。更直接的方式是,在聊天中输入一个测试性问题,观察 AI 的回复是否透露出它“知道”了历史信息(但这需要历史数据已存在)。
- 查看服务器日志 :你的
cursor-history-mcp服务器在运行时应该会输出日志。查看这些日志,确认:- 是否成功连接到 Cursor 日志文件。
- 是否成功解析到了对话消息并存入数据库。
- 是否收到了来自 Cursor 的 MCP 请求(如
read_resource)。
- 直接查询数据库 :使用 SQLite 客户端(如
sqlite3命令行工具或 DB Browser for SQLite)打开项目生成的.db文件,查看sessions和messages表中是否有数据。这是最直接的验证方式。 - 使用 MCP 调试工具 :Anthropic 提供了一些 MCP 调试工具,如
mcp-cli。你可以用它手动连接到你的服务器,发送list_resources和read_resource请求,模拟 Cursor 的行为,从而独立于编辑器调试你的服务器逻辑。
5. 高级用法、优化与问题排查
5.1 提升历史检索的相关性与效率
当历史数据积累到成千上万条时,如何快速、准确地找到对当前对话最有用的片段,成为挑战。
-
基于上下文的动态过滤 :
- 项目隔离 :这是最基本的过滤。始终将当前编辑器的项目路径(
project_path)作为检索的第一条件,避免其他项目的历史造成干扰。 - 文件关联 :在
read_resource请求中,尝试获取当前激活的编辑器标签页的文件路径。如果获取到,优先检索metadata字段中包含该文件路径的历史消息。这实现了“基于文件的上下文记忆”。 - 时间衰减权重 :在综合排序时,给较近的历史记录更高的权重,因为最近的讨论往往与当前任务连续性更强。
- 项目隔离 :这是最基本的过滤。始终将当前编辑器的项目路径(
-
摘要生成与压缩 :对于很久以前的长篇讨论,直接提供原始文本会占用大量 token。可以在后台运行一个摘要任务(使用本地轻量级 LLM 或高效的摘要算法),为每个会话或主题生成一个简短的摘要。当 AI 请求“项目上下文”时,返回这些摘要而非全文,能极大节省上下文窗口。
-
混合检索策略 :
- 关键词匹配(快) :首先利用
metadata中的文件名、函数名等进行快速筛选。 - 语义搜索(准) :对于更模糊的查询,使用向量数据库进行语义相似度搜索。
- 将两者的结果进行融合和重排序,兼顾速度和准确性。
- 关键词匹配(快) :首先利用
5.2 隐私、安全与数据管理
这是一个处理用户对话数据的工具,必须严肃对待隐私和安全。
- 明确的数据所有权 :所有数据存储在用户本地,不上传任何云端。这应该在项目的 README 中明确声明,让用户安心。
- 提供管理功能 :
- 查看与删除 :应该提供一个简单的命令行界面或图形化工具,让用户可以查看存储了哪些历史,并选择性删除某个会话或全部历史。
- 导出/导入 :允许用户导出历史数据为 JSON 等格式,方便备份或在其他机器上恢复。
- 全局开关 :在配置中提供选项,允许用户完全禁用历史记录功能。
- 敏感信息过滤(可选但建议) :在存储前,可以对内容进行简单的正则表达式扫描,尝试识别并遮盖(例如替换为
[KEY_REDACTED])类似密码、API Key、令牌等常见敏感信息模式。但这需要谨慎处理,避免误伤正常代码。
5.3 常见问题与故障排除实录
以下是我在搭建和使用类似工具时遇到的一些典型问题及解决方法:
问题1:Cursor 启动后,AI 完全没有“记忆”,服务器日志显示无连接或请求。
- 排查步骤 :
- 检查 MCP 配置路径 :确认
mcp.json中的args路径是 绝对路径 ,并且指向正确的、已构建的入口文件(如index.js)。这是最常见的错误。 - 检查服务器是否在运行 :在终端运行
ps aux | grep node(或 Windows 任务管理器),查看你的服务器进程是否存在。 - 检查 Cursor 日志 :查看 Cursor 自身的日志(可能与你的服务器监听的不是同一个文件),看是否有关于加载 MCP 服务器的错误信息。
- 验证服务器独立性 :使用
mcp-cli或写一个简单的脚本,直接连接你的服务器端口,手动发送list_resources请求,看服务器是否能正常响应。这可以排除 Cursor 配置问题,聚焦于服务器本身。
- 检查 MCP 配置路径 :确认
问题2:服务器能运行,但数据库里没有数据,或数据不完整。
- 排查步骤 :
- 确认日志路径 :检查
CURSOR_LOG_PATH环境变量是否指向了 Cursor 实际 写入日志的位置。不同版本、不同安装方式的 Cursor,日志路径可能不同。 - 验证日志格式 :手动打开 Cursor 的日志文件,触发几次 AI 对话,观察新日志的格式。确保你的解析器正则表达式或解析逻辑能匹配最新的格式。
- 检查文件监听权限 :确保你的服务器进程有权限读取目标日志文件。
- 查看解析器日志 :增加服务器解析环节的调试日志,打印出它从日志文件中读取到的原始行,以及解析后的结果,看是在哪一步出现了问题。
- 确认日志路径 :检查
问题3:历史记录被成功检索并提供了,但 AI 的回复似乎没有利用这些信息。
- 原因分析 :这可能不是服务器的问题,而是 AI 模型(客户端)如何使用上下文的问题。MCP 服务器只负责“提供”资源,至于 AI 模型是否、以及如何将这些资源内容纳入其思考过程,是由模型自身策略决定的。
- 应对策略 :
- 优化资源内容格式 :确保你返回的
text内容是高度结构化、易读的。例如,使用清晰的标记如[历史对话 - 2023-10-27] User: ... Assistant: ...,帮助 AI 快速理解。 - 提供更精准的资源 :尝试改进你的检索逻辑,只返回与当前问题 最相关 的 1-2 段历史,而不是一大堆可能不相关的记录。过量的、低相关性的上下文反而会干扰 AI。
- 在用户提问中引导 :作为用户,你可以在提问时明确指令,例如:“参考我们之前关于用户认证的讨论(你应该能在上下文中找到),现在遇到一个新问题...”。这能更明确地提示 AI 去使用已有的上下文。
- 优化资源内容格式 :确保你返回的
问题4:服务器运行一段时间后,性能下降或内存占用过高。
- 优化方向 :
- 数据库索引 :确保
sessions(project_path),messages(session_id, created_at)等常用查询字段上建立了索引。 - 向量搜索优化 :如果启用了语义搜索,考虑将向量索引存储在磁盘上,而不是全部加载到内存。或者使用更高效的向量数据库。
- 定期清理旧数据 :实现一个简单的清理任务,例如自动删除 30 天前的会话记录,或者允许用户设置历史保留期限。
- 日志监听优化 :确保文件监听器不会产生内存泄漏,对于不再需要的旧日志文件句柄要及时关闭。
- 数据库索引 :确保
将 cursor-history-mcp 这类工具融入工作流,初期可能需要一点调试成本,但一旦稳定运行,它带来的效率提升是显著的。你不再需要反复向 AI 解释项目背景,AI 更像一个真正有连续记忆的结对编程伙伴。它的价值会随着项目时间的增长和对话历史的积累而不断放大。开始可能会关注技术实现,但最终你会忘记它的存在,因为它已经无缝地成为了你开发环境里一个理所当然的“增强功能”。
更多推荐



所有评论(0)