ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

给 AI 安装高速缓存:实战 MCP 对接 Redis,实现热点数据的毫秒级读取与状态共享

给 AI 安装高速缓存:实战 MCP 对接 Redis,实现热点数据的毫秒级读取与状态共享 1. 为什么 AI 工具需要一个高速缓存层如果你正在用 Claude Code、Cursor 或者自己写的 Agent 跑多轮任务大概率遇到过这种场景同一个会话里反复读取同一份配置、同一段业务元数据每次都要走一遍文件系统或远程接口单次几十到几百毫秒十几次调用下来体感就明显卡顿。更麻烦的是多会话之间状态不共享——A 会话算出来的中间结果B 会话完全不知道只能重新算一遍。MCPModel Context Protocol解决的是「AI 工具怎么标准化调用外部能力」的问题但它本身不解决「数据放哪、读多快」的问题。MCP Server 默认是无状态的每次调用都是独立的一次请求-响应。想让 AI 拥有跨会话、跨工具的「瞬时记忆」就需要在 MCP Server 背后挂一个内存级存储Redis 是最顺手的选择毫秒级读写、天然支持 TTL 自动过期、还能做 Pub/Sub 通知。这篇要交付的是一个可复制的 MCP Redis 服务端骨架从 TypeScript 项目初始化到 MCP 工具定义再到 Redis 连接与热点数据读写函数最后给出本地验证步骤。适合已经了解 MCP 基本概念、想给 AI 工具加一层高速缓存的开发者。整套代码跑通后AI 通过 MCP 调用读写热点数据实测单次往返在 2ms 以内多会话共享同一份状态。2. 前置准备TaoToken 与 Redis 环境2.1 为什么这里会提到 TaoTokenMCP Server 本身不依赖任何模型服务但你要验证「AI 通过 MCP 调用 Redis」这条链路需要一个能挂载 MCP 工具的模型客户端。TaoToken 提供统一的 API 入口兼容主流模型调用格式适合用来做本地联调你可以在它的控制台拿到 API Key配到支持 MCP 的客户端里让模型真正去调用我们写的 Redis 工具。需要区分两件事MCP Server 是我们自己写的本地进程负责连 RedisTaoToken 是模型侧的接入点负责让 AI 发起工具调用。两者通过 MCP 协议在客户端里汇合。2.2 拿 Key 与确认接入信息进入控制台创建 API Key建议单独建一个用于本地开发的 Key方便随时吊销。接入文档里有不同客户端的配置示例照着填 base_url 和 key 即可。如果你用的是 Claude Code 这类编码工具也可以直接走 Coding Plan 的配置方式把 MCP Server 挂上去。控制台建 Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_redis_console接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_redis_docAPI Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_redis_apikeys2.3 Redis 本地环境本地起一个 Redis 最省事Docker 一行命令docker run -d --name mcp-redis -p 6379:6379 redis:7-alpine如果你已经有远程 Redis记下 host、port、password 即可。生产环境务必开密码并限制访问来源MCP Server 会以客户端身份连过去权限要收窄到只允许操作特定前缀的 key。3. 从零搭建 MCP Redis Server3.1 项目初始化mkdir mcp-redis-turbo cd mcp-redis-turbo npm init -y npm install modelcontextprotocol/sdk ioredis npm install -D typescript types/node tsx npx tsc --inittsconfig.json里把target改成ES2022module改成NodeNextmoduleResolution改成NodeNextoutDir设为dist。这样 ESM 导入不会报错。3.2 连接 Redis 并做命名空间隔离直接让 AI 操作裸 key 是危险的它可能覆盖掉别的业务数据。做法是在 Server 层强制加前缀AI 传进来的 key 只作为后缀。import Redis from ioredis; const NAMESPACE mcp:ai:context:; const redis new Redis({ host: process.env.REDIS_HOST || 127.0.0.1, port: Number(process.env.REDIS_PORT) || 6379, password: process.env.REDIS_PASSWORD || undefined, maxRetriesPerRequest: 2, connectTimeout: 3000, }); redis.on(error, (err) { console.error([redis] connection error:, err.message); }); function safeKey(raw: string): string { return NAMESPACE raw.replace(/[^a-zA-Z0-9:_-]/g, _); }safeKey做了两层保护加命名空间前缀过滤掉特殊字符防止 key 注入。maxRetriesPerRequest设小一点避免 Redis 挂掉时 MCP 调用长时间挂起。3.3 定义 MCP 工具我们暴露三个工具写缓存、读缓存、删缓存。每个工具的inputSchema要写清楚模型靠 description 判断什么时候调用。import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { ListToolsRequestSchema, CallToolRequestSchema, } from modelcontextprotocol/sdk/types.js; const server new Server( { name: turbo-cache-server, version: 1.0.0 }, { capabilities: { tools: {} } } ); server.setRequestHandler(ListToolsRequestSchema, async () ({ tools: [ { name: cache_set, description: 将高频或临时业务数据写入高速缓存适用于跨会话共享的状态、配置、中间计算结果。, inputSchema: { type: object, properties: { key: { type: string, description: 缓存键名如 task:status:001 }, value: { type: string, description: 待存储内容字符串或 JSON 字符串 }, ttl_seconds: { type: number, description: 过期时间秒默认 3600, default: 3600, }, }, required: [key, value], }, }, { name: cache_get, description: 从高速缓存毫秒级读取热点数据。, inputSchema: { type: object, properties: { key: { type: string, description: 缓存键名 }, }, required: [key], }, }, { name: cache_del, description: 删除指定缓存键用于状态失效或主动清理。, inputSchema: { type: object, properties: { key: { type: string, description: 缓存键名 }, }, required: [key], }, }, ], }));3.4 实现读写逻辑与异常处理server.setRequestHandler(CallToolRequestSchema, async (request) { const { name, arguments: args } request.params; try { if (name cache_set) { const key safeKey(String(args?.key)); const value String(args?.value); const ttl Number(args?.ttl_seconds) || 3600; await redis.set(key, value, EX, ttl); return { content: [{ type: text, text: 已写入 ${key}TTL ${ttl}s }], }; } if (name cache_get) { const key safeKey(String(args?.key)); const result await redis.get(key); return { content: [ { type: text, text: result ?? 缓存未命中或已过期, }, ], }; } if (name cache_del) { const key safeKey(String(args?.key)); const removed await redis.del(key); return { content: [{ type: text, text: removed ? 已删除 ${key} : 键不存在 ${key} }], }; } throw new Error(未知工具: ${name}); } catch (e) { const msg e instanceof Error ? e.message : String(e); return { content: [{ type: text, text: Redis 操作失败: ${msg} }], isError: true, }; } }); const transport new StdioServerTransport(); await server.connect(transport);注意isError: true这个字段模型看到它就知道这次调用失败了会决定是否重试或换策略。不设的话模型可能把错误信息当成正常结果继续往下走。3.5 编译与启动npx tsc node dist/index.js或者开发阶段直接用 tsxnpx tsx src/index.ts进程启动后不会有输出因为走的是 stdio 传输日志都打到 stderr。这是正常的MCP 客户端会接管 stdin/stdout 做协议通信。4. 挂到客户端并验证毫秒级读写4.1 客户端配置以支持 MCP 的客户端为例配置文件里加一段{ mcpServers: { turbo-cache: { command: node, args: [/absolute/path/to/mcp-redis-turbo/dist/index.js], env: { REDIS_HOST: 127.0.0.1, REDIS_PORT: 6379 } } } }路径必须用绝对路径相对路径在客户端启动子进程时解析基准不一样容易找不到文件。4.2 验证调用链路重启客户端后在对话里让模型执行一次写入和读取。比如帮我把 task:status:001 设为 runningTTL 60 秒然后读回来确认。模型会依次调用cache_set和cache_get。如果返回已写入 mcp:ai:context:task:status:001TTL 60s和running说明链路通了。想直接验证 Redis 侧可以另开终端docker exec -it mcp-redis redis-cli KEYS mcp:ai:context:* TTL mcp:ai:context:task:status:001 GET mcp:ai:context:task:status:0014.3 实测延迟本地 Redis 单次 GET 通常在 0.5ms 以内加上 MCP 协议序列化和 stdio 传输端到端一次工具调用在 2ms 左右。对比走文件系统读配置10-50ms或远程 HTTP 接口50-300ms高频场景下差距非常明显。多会话共享的验证方式开两个客户端会话A 写入一个 keyB 直接读同一个 key能读到就说明状态是共享的。这正是 Redis 作为「全局看板」的价值。5. 常见报错与排查5.1 ECONNREFUSED 127.0.0.1:6379Redis 没起来或者 host/port 配错。先确认容器在跑docker ps | grep mcp-redis如果容器在跑但连不上检查客户端配置里的env有没有正确传进去。MCP 子进程的环境变量不会自动继承父进程必须在配置里显式声明。5.2 工具列表为空客户端连上了 Server 但看不到工具通常是ListToolsRequestSchema的 handler 没注册成功或者编译产物是旧版本。重新npx tsc再重启客户端。另一个可能是capabilities里没声明tools: {}某些客户端会据此过滤。5.3 写入成功但读不到先看 key 前缀对不对。AI 传的 key 经过safeKey处理后会带mcp:ai:context:前缀如果你在 redis-cli 里直接GET task:status:001是读不到的要带上完整前缀。另外检查 TTL 是不是设太短写入后几秒就过期了。5.4 中文或特殊字符导致 key 异常safeKey里的正则会把非字母数字冒号下划线横线的字符替换成下划线。如果你的业务 key 本身含中文建议在 AI 侧就用英文或拼音避免替换后 key 冲突。5.5 模型不调用工具模型不知道有这些工具或者 description 写得不够清楚。检查客户端是否成功加载了 MCP Server以及工具描述里有没有说明「什么时候该用」。描述里写清楚适用场景模型判断会更准。6. 继续往下走这套骨架跑通后可以按需扩展。比如加一个cache_incr工具做计数器或者用 Redis Pub/Sub 做跨会话通知——当某个 key 变化时主动推给客户端。再进一步可以在 Server 层加布隆过滤器拦截不存在的 key 查询避免 AI 陷入无效循环。如果你还没配好模型侧的接入先去控制台建个 Key把 MCP Server 挂到客户端里跑一遍完整链路。编码场景建议直接走 Coding Plan省去逐个客户端配置的麻烦。模型对话入口可以用来快速验证工具调用是否正常不用每次都开完整项目。模型对话验证https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_redis_chatCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_redis_codingplan接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmcp_redis_doc2最后提醒一句Redis 里别放敏感数据。即便是内网部署也要在写入前做一层过滤密码、完整身份信息这类内容不要进缓存。TTL 该设就设别让临时状态变成永久垃圾。
返回列表