redis-crud-server
Advanced tools
+139
-957
| #!/usr/bin/env node | ||
| /** | ||
| * Redis CRUD MCP 服务器 | ||
| * 提供用于 Redis 数据库操作的工具:SET、GET、DEL、EXISTS | ||
| * Redis CRUD MCP 服务器 - 自动检测项目目录版 | ||
| * | ||
| * 特性: | ||
| * 1. 自动向上查找 .env 文件定位项目根目录 | ||
| * 2. 支持环境变量 PROJECT_DIR 手动指定 | ||
| * 3. 兼容 Claude Code、Codex 等多种工具 | ||
| * 4. 多项目连接隔离 | ||
| */ | ||
@@ -16,22 +21,37 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; | ||
| const __dirname = path.dirname(__filename); | ||
| // Redis 客户端 | ||
| let redisClient; | ||
| /** | ||
| * 解析 INI 格式的配置文件 | ||
| */ | ||
| // ==================== 项目目录检测 ==================== | ||
| function findProjectRoot(startDir) { | ||
| let dir = startDir; | ||
| for (let i = 0; i < 10; i++) { | ||
| if (fs.existsSync(path.join(dir, '.env'))) | ||
| return dir; | ||
| const parentDir = path.dirname(dir); | ||
| if (parentDir === dir) | ||
| break; | ||
| dir = parentDir; | ||
| } | ||
| return startDir; | ||
| } | ||
| function getProjectDir() { | ||
| if (process.env.PROJECT_DIR) | ||
| return process.env.PROJECT_DIR; | ||
| if (process.env.MCP_PROJECT_DIR) | ||
| return process.env.MCP_PROJECT_DIR; | ||
| return findProjectRoot(process.cwd()); | ||
| } | ||
| // ==================== 连接管理 ==================== | ||
| const clientCache = new Map(); | ||
| const OPERATION_TIMEOUT = parseInt(process.env.REDIS_TIMEOUT || '10000'); // 默认 10 秒 | ||
| function parseIniConfig(filePath) { | ||
| const config = {}; | ||
| try { | ||
| if (!fs.existsSync(filePath)) { | ||
| if (!fs.existsSync(filePath)) | ||
| return config; | ||
| } | ||
| const content = fs.readFileSync(filePath, 'utf-8'); | ||
| // 统一换行符,支持 Windows (\r\n)、Unix (\n) 和旧 Mac (\r) | ||
| const content = fs.readFileSync(filePath, 'utf-8').replace(/\r\n/g, '\n').replace(/\r/g, '\n'); | ||
| let currentSection = ''; | ||
| content.split('\n').forEach(line => { | ||
| line = line.trim(); | ||
| // 跳过空行和注释 | ||
| if (!line || line.startsWith(';') || line.startsWith('#')) { | ||
| if (!line || line.startsWith(';') || line.startsWith('#')) | ||
| return; | ||
| } | ||
| // 检查是否是 section 标题 | ||
| const sectionMatch = line.match(/^\[([^\]]+)\]$/); | ||
@@ -43,977 +63,139 @@ if (sectionMatch) { | ||
| } | ||
| // 解析 key=value | ||
| const keyValueMatch = line.match(/^([^=]+)=(.*)$/); | ||
| if (keyValueMatch && currentSection) { | ||
| const key = keyValueMatch[1].trim(); | ||
| const value = keyValueMatch[2].trim(); | ||
| config[currentSection][key] = value; | ||
| } | ||
| if (keyValueMatch && currentSection) | ||
| config[currentSection][keyValueMatch[1].trim()] = keyValueMatch[2].trim(); | ||
| }); | ||
| } | ||
| catch (error) { | ||
| // 忽略解析错误 | ||
| } | ||
| catch (error) { } | ||
| return config; | ||
| } | ||
| /** | ||
| * 获取 Redis 连接配置 - 支持项目级 .env 文件和环境变量 | ||
| * 支持两种格式: | ||
| * 1. KEY=VALUE 格式(标准 .env) | ||
| * 2. INI 格式([REDIS] 或 [DATABASE] section) | ||
| */ | ||
| function getRedisConfig() { | ||
| // 优先级 1: 尝试读取 .env 文件 | ||
| // 优先级顺序:当前工作目录 > ENV_PATH环境变量 > 源代码相对路径 | ||
| const envPath = process.env.ENV_PATH || path.join(process.cwd(), '.env'); | ||
| // 首先尝试解析 INI 格式 | ||
| function getRedisConfig(projectDir) { | ||
| const envPath = path.join(projectDir, '.env'); | ||
| const iniConfig = parseIniConfig(envPath); | ||
| // 然后加载标准 .env 格式 | ||
| dotenv.config({ path: envPath }); | ||
| // 定义可能的配置名称映射(支持多种命名约定) | ||
| const configNameMappings = { | ||
| const redisEnvKeys = ['REDIS_HOST', 'REDIS_SERVER_HOST', 'REDIS_HOSTNAME', 'HOST', 'HOSTNAME', | ||
| 'REDIS_PORT', 'REDIS_SERVER_PORT', 'HOSTPORT', 'PORT', | ||
| 'REDIS_PASSWORD', 'REDIS_SERVER_PASSWORD', 'PASSWORD', | ||
| 'REDIS_DB', 'SELECT', 'DATABASE_INDEX', 'DB_INDEX']; | ||
| const originalEnv = {}; | ||
| redisEnvKeys.forEach(k => originalEnv[k] = process.env[k]); | ||
| if (fs.existsSync(envPath)) | ||
| dotenv.config({ path: envPath }); | ||
| const mappings = { | ||
| host: ['REDIS_HOST', 'REDIS_SERVER_HOST', 'REDIS_HOSTNAME', 'HOST', 'HOSTNAME'], | ||
| port: ['REDIS_PORT', 'REDIS_SERVER_PORT', 'HOSTPORT', 'PORT'], | ||
| port: ['REDIS_PORT', 'REDIS_SERVER_PORT', 'PORT'], | ||
| password: ['REDIS_PASSWORD', 'REDIS_SERVER_PASSWORD', 'PASSWORD'], | ||
| db: ['REDIS_DB', 'SELECT', 'DATABASE_INDEX', 'DB_INDEX'] | ||
| }; | ||
| // 尝试从环境变量中获取配置值 | ||
| function getConfigValue(configKey) { | ||
| const possibleNames = configNameMappings[configKey]; | ||
| if (!possibleNames) | ||
| const getEnvValue = (key) => { | ||
| const names = mappings[key]; | ||
| if (!names) | ||
| return undefined; | ||
| for (const name of possibleNames) { | ||
| if (process.env[name]) { | ||
| for (const name of names) { | ||
| if (process.env[name]) | ||
| return process.env[name]; | ||
| } | ||
| } | ||
| return undefined; | ||
| } | ||
| // 尝试从 INI 配置中获取值 | ||
| function getIniConfigValue(configKey) { | ||
| // 支持 [REDIS] 和 [DATABASE] 两种 section | ||
| const sections = ['REDIS', 'DATABASE']; | ||
| for (const section of sections) { | ||
| if (iniConfig[section]) { | ||
| const possibleNames = configNameMappings[configKey]; | ||
| if (possibleNames) { | ||
| for (const name of possibleNames) { | ||
| if (iniConfig[section][name]) { | ||
| return iniConfig[section][name]; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| // INI 配置只从 REDIS section 读取,避免与 DATABASE section 混淆 | ||
| const getIniValue = (key) => { | ||
| const names = mappings[key]; | ||
| if (!names || !iniConfig.REDIS) | ||
| return undefined; | ||
| for (const name of names) { | ||
| if (iniConfig.REDIS[name]) | ||
| return iniConfig.REDIS[name]; | ||
| } | ||
| return undefined; | ||
| } | ||
| // 获取所有配置值(优先级:环境变量 > INI 配置 > 默认值) | ||
| const host = getConfigValue('host') || getIniConfigValue('host') || '127.0.0.1'; | ||
| const port = getConfigValue('port') || getIniConfigValue('port') || '6379'; | ||
| const password = getConfigValue('password') || getIniConfigValue('password'); | ||
| const dbStr = getConfigValue('db') || getIniConfigValue('db') || '0'; | ||
| const db = parseInt(dbStr, 10); | ||
| // 检查是否有密码被设置 | ||
| }; | ||
| const host = getEnvValue('host') || getIniValue('host') || '127.0.0.1'; | ||
| const port = getEnvValue('port') || getIniValue('port') || '6379'; | ||
| const password = getEnvValue('password') || getIniValue('password'); | ||
| const dbStr = getEnvValue('db') || getIniValue('db') || '0'; | ||
| redisEnvKeys.forEach(k => originalEnv[k] === undefined ? delete process.env[k] : process.env[k] = originalEnv[k]); | ||
| if (!password) { | ||
| throw new Error(`Redis CRUD MCP 服务器需要配置密码。\n\n` + | ||
| `配置方式(优先级顺序):\n\n` + | ||
| `1. 项目级 .env 文件(推荐)\n` + | ||
| ` - 在项目根目录创建 .env 文件\n` + | ||
| ` - 支持两种格式:\n\n` + | ||
| ` 格式一:标准 KEY=VALUE 格式\n` + | ||
| ` REDIS_HOST=127.0.0.1\n` + | ||
| ` REDIS_PORT=6379\n` + | ||
| ` REDIS_PASSWORD=your_password\n` + | ||
| ` REDIS_DB=0\n\n` + | ||
| ` 格式二:INI 格式([REDIS] 或 [DATABASE] section)\n` + | ||
| ` [REDIS]\n` + | ||
| ` REDIS_HOSTNAME=127.0.0.1\n` + | ||
| ` PORT=6379\n` + | ||
| ` REDIS_PASSWORD=your_password\n` + | ||
| ` SELECT=0\n\n` + | ||
| ` 或\n\n` + | ||
| ` [DATABASE]\n` + | ||
| ` HOSTNAME=127.0.0.1\n` + | ||
| ` HOSTPORT=6379\n` + | ||
| ` PASSWORD=your_password\n` + | ||
| ` SELECT=0\n\n` + | ||
| ` 支持的配置名称(任选其一):\n` + | ||
| ` • 主机: REDIS_HOST / REDIS_SERVER_HOST / HOST / HOSTNAME / REDIS_HOSTNAME\n` + | ||
| ` • 端口: REDIS_PORT / REDIS_SERVER_PORT / PORT / HOSTPORT\n` + | ||
| ` • 密码: REDIS_PASSWORD / REDIS_SERVER_PASSWORD / PASSWORD\n` + | ||
| ` • 数据库: REDIS_DB / SELECT / DATABASE_INDEX / DB_INDEX (默认: 0)\n\n` + | ||
| `2. 全局 MCP 配置文件\n` + | ||
| ` - 复制项目中的 cline_mcp_settings.example.json 文件\n` + | ||
| ` - 编辑其中的 Redis 配置信息\n` + | ||
| ` - 将配置添加到您的 cline_mcp_settings.json 文件中\n` + | ||
| ` - 配置位置: %APPDATA%\\Code\\User\\globalStorage\\saoudrizwan.claude-dev\\settings\\cline_mcp_settings.json\n\n` + | ||
| `或者运行 install.cjs 脚本进行自动配置。`); | ||
| throw new Error(`Redis 配置不完整。项目目录: ${projectDir}\n请检查 .env 文件中的 Redis 配置。`); | ||
| } | ||
| return { | ||
| host, | ||
| port: parseInt(port), | ||
| password, | ||
| db: isNaN(db) ? 0 : db, | ||
| }; | ||
| return { host, port: parseInt(port), password, db: parseInt(dbStr) || 0, socket: { connectTimeout: OPERATION_TIMEOUT } }; | ||
| } | ||
| /** | ||
| * 初始化 Redis 连接 | ||
| */ | ||
| async function initializeRedis() { | ||
| // 超时包装函数 | ||
| function withTimeout(promise, ms, message) { | ||
| return Promise.race([ | ||
| promise, | ||
| new Promise((_, reject) => setTimeout(() => reject(new Error(message)), ms)) | ||
| ]); | ||
| } | ||
| async function getClient(projectDir) { | ||
| if (clientCache.has(projectDir)) | ||
| return clientCache.get(projectDir); | ||
| const config = getRedisConfig(projectDir); | ||
| const client = createClient({ | ||
| url: `redis://:${config.password}@${config.host}:${config.port}/${config.db}`, | ||
| socket: { connectTimeout: OPERATION_TIMEOUT } | ||
| }); | ||
| client.on('error', (err) => console.error('Redis 错误:', err)); | ||
| // 显式连接并带超时保护 | ||
| try { | ||
| const config = getRedisConfig(); | ||
| redisClient = createClient({ | ||
| url: `redis://:${config.password}@${config.host}:${config.port}/${config.db}` | ||
| }); | ||
| redisClient.on('error', (err) => console.error('Redis 连接错误:', err)); | ||
| await redisClient.connect(); | ||
| console.error(`Redis 连接成功 (数据库: ${config.db})`); | ||
| await withTimeout(client.connect(), OPERATION_TIMEOUT, `Redis 连接超时 (${OPERATION_TIMEOUT}ms)`); | ||
| console.error(`Redis 连接成功 [${projectDir}] -> ${config.host}:${config.port}/${config.db}`); | ||
| } | ||
| catch (error) { | ||
| console.error('初始化 Redis 连接失败:', error); | ||
| console.error(`Redis 连接失败:`, error); | ||
| throw error; | ||
| } | ||
| clientCache.set(projectDir, client); | ||
| return client; | ||
| } | ||
| /** | ||
| * 验证 Redis 操作参数 | ||
| */ | ||
| function validateRedisArgs(args) { | ||
| return (typeof args === 'object' && | ||
| args !== null && | ||
| typeof args.key === 'string' && | ||
| (args.value === undefined || typeof args.value === 'string') && | ||
| (args.field === undefined || typeof args.field === 'string') && | ||
| (args.fields === undefined || Array.isArray(args.fields)) && | ||
| (args.values === undefined || Array.isArray(args.values)) && | ||
| (args.members === undefined || Array.isArray(args.members)) && | ||
| (args.member === undefined || typeof args.member === 'string') && | ||
| (args.score === undefined || typeof args.score === 'number') && | ||
| (args.start === undefined || typeof args.start === 'number') && | ||
| (args.stop === undefined || typeof args.stop === 'number') && | ||
| (args.min === undefined || typeof args.min === 'number') && | ||
| (args.max === undefined || typeof args.max === 'number')); | ||
| } | ||
| /** | ||
| * 执行 SET 操作 | ||
| */ | ||
| async function executeSet(key, value) { | ||
| return await redisClient.set(key, value); | ||
| } | ||
| /** | ||
| * 执行 GET 操作 | ||
| */ | ||
| async function executeGet(key) { | ||
| return await redisClient.get(key); | ||
| } | ||
| /** | ||
| * 执行 DEL 操作 | ||
| */ | ||
| async function executeDel(key) { | ||
| return await redisClient.del(key); | ||
| } | ||
| /** | ||
| * 执行 EXISTS 操作 | ||
| */ | ||
| async function executeExists(key) { | ||
| return await redisClient.exists(key); | ||
| } | ||
| /** | ||
| * 执行 LPUSH 操作 (列表左侧推入) | ||
| */ | ||
| async function executeLPush(key, values) { | ||
| return await redisClient.lPush(key, values); | ||
| } | ||
| /** | ||
| * 执行 RPUSH 操作 (列表右侧推入) | ||
| */ | ||
| async function executeRPush(key, values) { | ||
| return await redisClient.rPush(key, values); | ||
| } | ||
| /** | ||
| * 执行 LPOP 操作 (列表左侧弹出) | ||
| */ | ||
| async function executeLPop(key) { | ||
| return await redisClient.lPop(key); | ||
| } | ||
| /** | ||
| * 执行 RPOP 操作 (列表右侧弹出) | ||
| */ | ||
| async function executeRPop(key) { | ||
| return await redisClient.rPop(key); | ||
| } | ||
| /** | ||
| * 执行 LRANGE 操作 (获取列表范围) | ||
| */ | ||
| async function executeLRange(key, start, stop) { | ||
| return await redisClient.lRange(key, start, stop); | ||
| } | ||
| /** | ||
| * 执行 LLEN 操作 (获取列表长度) | ||
| */ | ||
| async function executeLLen(key) { | ||
| return await redisClient.lLen(key); | ||
| } | ||
| /** | ||
| * 执行 SADD 操作 (集合添加成员) | ||
| */ | ||
| async function executeSAdd(key, members) { | ||
| return await redisClient.sAdd(key, members); | ||
| } | ||
| /** | ||
| * 执行 SREM 操作 (集合移除成员) | ||
| */ | ||
| async function executeSRem(key, members) { | ||
| return await redisClient.sRem(key, members); | ||
| } | ||
| /** | ||
| * 执行 SMEMBERS 操作 (获取集合所有成员) | ||
| */ | ||
| async function executeSMembers(key) { | ||
| return await redisClient.sMembers(key); | ||
| } | ||
| /** | ||
| * 执行 SISMEMBER 操作 (检查成员是否在集合中) | ||
| */ | ||
| async function executeSIsMember(key, member) { | ||
| return await redisClient.sIsMember(key, member); | ||
| } | ||
| /** | ||
| * 执行 HSET 操作 (哈希设置字段) | ||
| */ | ||
| async function executeHSet(key, field, value) { | ||
| return await redisClient.hSet(key, field, value); | ||
| } | ||
| /** | ||
| * 执行 HGET 操作 (哈希获取字段) | ||
| */ | ||
| async function executeHGet(key, field) { | ||
| return await redisClient.hGet(key, field); | ||
| } | ||
| /** | ||
| * 执行 HGETALL 操作 (获取哈希所有字段和值) | ||
| */ | ||
| async function executeHGetAll(key) { | ||
| return await redisClient.hGetAll(key); | ||
| } | ||
| /** | ||
| * 执行 HDEL 操作 (哈希删除字段) | ||
| */ | ||
| async function executeHDel(key, fields) { | ||
| return await redisClient.hDel(key, fields); | ||
| } | ||
| /** | ||
| * 执行 ZADD 操作 (有序集合添加成员) | ||
| */ | ||
| async function executeZAdd(key, score, member) { | ||
| return await redisClient.zAdd(key, { score, value: member }); | ||
| } | ||
| /** | ||
| * 执行 ZREM 操作 (有序集合移除成员) | ||
| */ | ||
| async function executeZRem(key, members) { | ||
| return await redisClient.zRem(key, members); | ||
| } | ||
| /** | ||
| * 执行 ZRANGE 操作 (有序集合范围查询) | ||
| */ | ||
| async function executeZRange(key, min, max) { | ||
| return await redisClient.zRange(key, min, max); | ||
| } | ||
| /** | ||
| * 创建带有 Redis CRUD 操作工具的 MCP 服务器 | ||
| */ | ||
| const server = new Server({ | ||
| name: "redis-crud-server", | ||
| version: "0.1.0", | ||
| }, { | ||
| capabilities: { | ||
| tools: {}, | ||
| }, | ||
| }); | ||
| /** | ||
| * 列出可用 Redis CRUD 工具的处理器 | ||
| */ | ||
| server.setRequestHandler(ListToolsRequestSchema, async () => { | ||
| return { | ||
| tools: [ | ||
| // 字符串操作 | ||
| { | ||
| name: "redis_set", | ||
| description: "在 Redis 数据库中设置字符串键值对", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "要设置的键" | ||
| }, | ||
| value: { | ||
| type: "string", | ||
| description: "要设置的值" | ||
| } | ||
| }, | ||
| required: ["key", "value"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_get", | ||
| description: "从 Redis 数据库中获取字符串键的值", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "要获取的键" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| // 通用操作 | ||
| { | ||
| name: "redis_del", | ||
| description: "从 Redis 数据库中删除键", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "要删除的键" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_exists", | ||
| description: "检查 Redis 数据库中键是否存在", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "要检查的键" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| // 列表操作 | ||
| { | ||
| name: "redis_lpush", | ||
| description: "从列表左侧推入元素", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "列表键" | ||
| }, | ||
| values: { | ||
| type: "array", | ||
| items: { type: "string" }, | ||
| description: "要推入的值数组" | ||
| } | ||
| }, | ||
| required: ["key", "values"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_rpush", | ||
| description: "从列表右侧推入元素", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "列表键" | ||
| }, | ||
| values: { | ||
| type: "array", | ||
| items: { type: "string" }, | ||
| description: "要推入的值数组" | ||
| } | ||
| }, | ||
| required: ["key", "values"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_lpop", | ||
| description: "从列表左侧弹出元素", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "列表键" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_rpop", | ||
| description: "从列表右侧弹出元素", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "列表键" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_lrange", | ||
| description: "获取列表指定范围的元素", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "列表键" | ||
| }, | ||
| start: { | ||
| type: "number", | ||
| description: "起始索引", | ||
| default: 0 | ||
| }, | ||
| stop: { | ||
| type: "number", | ||
| description: "结束索引", | ||
| default: -1 | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_llen", | ||
| description: "获取列表长度", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "列表键" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| // 集合操作 | ||
| { | ||
| name: "redis_sadd", | ||
| description: "向集合添加成员", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "集合键" | ||
| }, | ||
| members: { | ||
| type: "array", | ||
| items: { type: "string" }, | ||
| description: "要添加的成员数组" | ||
| } | ||
| }, | ||
| required: ["key", "members"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_srem", | ||
| description: "从集合移除成员", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "集合键" | ||
| }, | ||
| members: { | ||
| type: "array", | ||
| items: { type: "string" }, | ||
| description: "要移除的成员数组" | ||
| } | ||
| }, | ||
| required: ["key", "members"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_smembers", | ||
| description: "获取集合的所有成员", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "集合键" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_sismember", | ||
| description: "检查成员是否在集合中", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "集合键" | ||
| }, | ||
| member: { | ||
| type: "string", | ||
| description: "要检查的成员" | ||
| } | ||
| }, | ||
| required: ["key", "member"] | ||
| } | ||
| }, | ||
| // 哈希操作 | ||
| { | ||
| name: "redis_hset", | ||
| description: "设置哈希字段的值", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "哈希键" | ||
| }, | ||
| field: { | ||
| type: "string", | ||
| description: "字段名" | ||
| }, | ||
| value: { | ||
| type: "string", | ||
| description: "字段值" | ||
| } | ||
| }, | ||
| required: ["key", "field", "value"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_hget", | ||
| description: "获取哈希字段的值", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "哈希键" | ||
| }, | ||
| field: { | ||
| type: "string", | ||
| description: "字段名" | ||
| } | ||
| }, | ||
| required: ["key", "field"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_hgetall", | ||
| description: "获取哈希的所有字段和值", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "哈希键" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_hdel", | ||
| description: "删除哈希字段", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "哈希键" | ||
| }, | ||
| fields: { | ||
| type: "array", | ||
| items: { type: "string" }, | ||
| description: "要删除的字段数组" | ||
| } | ||
| }, | ||
| required: ["key", "fields"] | ||
| } | ||
| }, | ||
| // 有序集合操作 | ||
| { | ||
| name: "redis_zadd", | ||
| description: "向有序集合添加成员", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "有序集合键" | ||
| }, | ||
| score: { | ||
| type: "number", | ||
| description: "成员分数" | ||
| }, | ||
| member: { | ||
| type: "string", | ||
| description: "成员值" | ||
| } | ||
| }, | ||
| required: ["key", "score", "member"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_zrem", | ||
| description: "从有序集合移除成员", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "有序集合键" | ||
| }, | ||
| members: { | ||
| type: "array", | ||
| items: { type: "string" }, | ||
| description: "要移除的成员数组" | ||
| } | ||
| }, | ||
| required: ["key", "members"] | ||
| } | ||
| }, | ||
| { | ||
| name: "redis_zrange", | ||
| description: "获取有序集合指定分数范围的成员", | ||
| inputSchema: { | ||
| type: "object", | ||
| properties: { | ||
| key: { | ||
| type: "string", | ||
| description: "有序集合键" | ||
| }, | ||
| min: { | ||
| type: "number", | ||
| description: "最小分数", | ||
| default: "-inf" | ||
| }, | ||
| max: { | ||
| type: "number", | ||
| description: "最大分数", | ||
| default: "+inf" | ||
| } | ||
| }, | ||
| required: ["key"] | ||
| } | ||
| } | ||
| ] | ||
| }; | ||
| }); | ||
| /** | ||
| * Redis CRUD 工具调用的处理器 | ||
| */ | ||
| // ==================== MCP 服务器 ==================== | ||
| const server = new Server({ name: "redis-crud-server", version: "1.3.0" }, { capabilities: { tools: {} } }); | ||
| server.setRequestHandler(ListToolsRequestSchema, async () => ({ | ||
| tools: [ | ||
| { name: "redis_set", description: "设置键值。自动读取项目 .env 配置连接 Redis。", inputSchema: { type: "object", properties: { key: { type: "string" }, value: { type: "string" } }, required: ["key", "value"] } }, | ||
| { name: "redis_get", description: "获取键值。", inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] } }, | ||
| { name: "redis_del", description: "删除键。", inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] } }, | ||
| { name: "redis_exists", description: "检查键是否存在。", inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] } }, | ||
| { name: "redis_info", description: "获取连接信息。", inputSchema: { type: "object", properties: {} } }, | ||
| { name: "redis_hset", description: "设置哈希字段。", inputSchema: { type: "object", properties: { key: { type: "string" }, field: { type: "string" }, value: { type: "string" } }, required: ["key", "field", "value"] } }, | ||
| { name: "redis_hget", description: "获取哈希字段。", inputSchema: { type: "object", properties: { key: { type: "string" }, field: { type: "string" } }, required: ["key", "field"] } }, | ||
| { name: "redis_hgetall", description: "获取哈希所有字段。", inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] } }, | ||
| { name: "redis_hdel", description: "删除哈希字段。", inputSchema: { type: "object", properties: { key: { type: "string" }, fields: { type: "array", items: { type: "string" } } }, required: ["key", "fields"] } } | ||
| ] | ||
| })); | ||
| server.setRequestHandler(CallToolRequestSchema, async (request) => { | ||
| if (!validateRedisArgs(request.params.arguments)) { | ||
| throw new McpError(ErrorCode.InvalidParams, '无效的参数。必需参数:key (字符串),其他参数根据操作类型而定'); | ||
| } | ||
| const args = request.params.arguments; | ||
| const { key } = args; | ||
| try { | ||
| let result; | ||
| switch (request.params.name) { | ||
| // 字符串操作 | ||
| case "redis_set": | ||
| if (!args.value) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'SET 操作需要提供 value 参数'); | ||
| } | ||
| result = await executeSet(key, args.value); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `SET 执行成功。键: ${key}, 值: ${args.value}, 结果: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_get": | ||
| result = await executeGet(key); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `GET 执行成功。键: ${key}, 值: ${result !== null ? result : 'null'}` | ||
| } | ||
| ] | ||
| }; | ||
| // 通用操作 | ||
| case "redis_del": | ||
| result = await executeDel(key); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `DEL 执行成功。键: ${key}, 删除的键数量: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_exists": | ||
| result = await executeExists(key); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `EXISTS 执行成功。键: ${key}, 存在: ${result > 0 ? '是' : '否'}` | ||
| } | ||
| ] | ||
| }; | ||
| // 列表操作 | ||
| case "redis_lpush": | ||
| if (!args.values) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'LPUSH 操作需要提供 values 参数'); | ||
| } | ||
| result = await executeLPush(key, args.values); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `LPUSH 执行成功。键: ${key}, 添加的元素数量: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_rpush": | ||
| if (!args.values) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'RPUSH 操作需要提供 values 参数'); | ||
| } | ||
| result = await executeRPush(key, args.values); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `RPUSH 执行成功。键: ${key}, 添加的元素数量: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_lpop": | ||
| result = await executeLPop(key); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `LPOP 执行成功。键: ${key}, 弹出的元素: ${result !== null ? result : 'null'}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_rpop": | ||
| result = await executeRPop(key); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `RPOP 执行成功。键: ${key}, 弹出的元素: ${result !== null ? result : 'null'}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_lrange": | ||
| const start = args.start || 0; | ||
| const stop = args.stop || -1; | ||
| result = await executeLRange(key, start, stop); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `LRANGE 执行成功。键: ${key}, 范围: [${start}, ${stop}], 结果: [${result.join(', ')}]` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_llen": | ||
| result = await executeLLen(key); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `LLEN 执行成功。键: ${key}, 长度: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| // 集合操作 | ||
| case "redis_sadd": | ||
| if (!args.members) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'SADD 操作需要提供 members 参数'); | ||
| } | ||
| result = await executeSAdd(key, args.members); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `SADD 执行成功。键: ${key}, 添加的成员数量: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_srem": | ||
| if (!args.members) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'SREM 操作需要提供 members 参数'); | ||
| } | ||
| result = await executeSRem(key, args.members); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `SREM 执行成功。键: ${key}, 移除的成员数量: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_smembers": | ||
| result = await executeSMembers(key); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `SMEMBERS 执行成功。键: ${key}, 成员: [${result.join(', ')}]` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_sismember": | ||
| if (!args.member) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'SISMEMBER 操作需要提供 member 参数'); | ||
| } | ||
| result = await executeSIsMember(key, args.member); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `SISMEMBER 执行成功。键: ${key}, 成员: ${args.member}, 是否存在: ${result ? '是' : '否'}` | ||
| } | ||
| ] | ||
| }; | ||
| // 哈希操作 | ||
| case "redis_hset": | ||
| if (!args.field || !args.value) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'HSET 操作需要提供 field 和 value 参数'); | ||
| } | ||
| result = await executeHSet(key, args.field, args.value); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `HSET 执行成功。键: ${key}, 字段: ${args.field}, 值: ${args.value}, 结果: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_hget": | ||
| if (!args.field) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'HGET 操作需要提供 field 参数'); | ||
| } | ||
| result = await executeHGet(key, args.field); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `HGET 执行成功。键: ${key}, 字段: ${args.field}, 值: ${result !== undefined ? result : 'undefined'}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_hgetall": | ||
| result = await executeHGetAll(key); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `HGETALL 执行成功。键: ${key}, 数据: ${JSON.stringify(result)}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_hdel": | ||
| if (!args.fields) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'HDEL 操作需要提供 fields 参数'); | ||
| } | ||
| result = await executeHDel(key, args.fields); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `HDEL 执行成功。键: ${key}, 删除的字段数量: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| // 有序集合操作 | ||
| case "redis_zadd": | ||
| if (args.score === undefined || !args.member) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'ZADD 操作需要提供 score 和 member 参数'); | ||
| } | ||
| result = await executeZAdd(key, args.score, args.member); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `ZADD 执行成功。键: ${key}, 分数: ${args.score}, 成员: ${args.member}, 结果: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_zrem": | ||
| if (!args.members) { | ||
| throw new McpError(ErrorCode.InvalidParams, 'ZREM 操作需要提供 members 参数'); | ||
| } | ||
| result = await executeZRem(key, args.members); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `ZREM 执行成功。键: ${key}, 移除的成员数量: ${result}` | ||
| } | ||
| ] | ||
| }; | ||
| case "redis_zrange": | ||
| const min = args.min || 0; | ||
| const max = args.max || -1; | ||
| result = await executeZRange(key, min, max); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `ZRANGE 执行成功。键: ${key}, 范围: [${min}, ${max}], 结果: [${result.join(', ')}]` | ||
| } | ||
| ] | ||
| }; | ||
| default: | ||
| throw new McpError(ErrorCode.MethodNotFound, `未知工具: ${request.params.name}`); | ||
| } | ||
| const projectDir = getProjectDir(); | ||
| const client = await getClient(projectDir); | ||
| const args = request.params.arguments; | ||
| const ops = { | ||
| redis_info: async () => `Redis MCP 信息\n项目目录: ${projectDir}\n连接数: ${clientCache.size}`, | ||
| redis_set: async () => { const r = await client.set(args.key, args.value); return `SET 成功。键: ${args.key}`; }, | ||
| redis_get: async () => { const r = await client.get(args.key); return `GET 成功。键: ${args.key}, 值: ${r ?? 'null'}`; }, | ||
| redis_del: async () => { const r = await client.del(args.key); return `DEL 成功。删除 ${r} 个键`; }, | ||
| redis_exists: async () => { const r = await client.exists(args.key); return `EXISTS 成功。键 ${args.key} ${r > 0 ? '存在' : '不存在'}`; }, | ||
| redis_hset: async () => { await client.hSet(args.key, args.field, args.value); return `HSET 成功。${args.key}.${args.field} = ${args.value}`; }, | ||
| redis_hget: async () => { const r = await client.hGet(args.key, args.field); return `HGET 成功。${args.key}.${args.field} = ${r ?? 'null'}`; }, | ||
| redis_hgetall: async () => { const r = await client.hGetAll(args.key); return `HGETALL 成功。${args.key}: ${JSON.stringify(r)}`; }, | ||
| redis_hdel: async () => { const r = await client.hDel(args.key, args.fields); return `HDEL 成功。删除 ${r} 个字段`; } | ||
| }; | ||
| if (!ops[request.params.name]) | ||
| throw new McpError(ErrorCode.MethodNotFound, `未知工具: ${request.params.name}`); | ||
| return { content: [{ type: "text", text: await ops[request.params.name]() }] }; | ||
| } | ||
| catch (error) { | ||
| console.error(`Redis 操作失败:`, error); | ||
| return { | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `Redis 操作失败: ${error instanceof Error ? error.message : String(error)}` | ||
| } | ||
| ], | ||
| isError: true | ||
| }; | ||
| // 连接错误时清除缓存,下次请求重新创建连接 | ||
| const projectDir = getProjectDir(); | ||
| if (clientCache.has(projectDir)) { | ||
| const client = clientCache.get(projectDir); | ||
| clientCache.delete(projectDir); | ||
| client.quit().catch(() => { }); | ||
| } | ||
| return { content: [{ type: "text", text: `操作失败: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; | ||
| } | ||
| }); | ||
| /** | ||
| * 使用 stdio 传输启动服务器 | ||
| */ | ||
| async function main() { | ||
| try { | ||
| await initializeRedis(); | ||
| const transport = new StdioServerTransport(); | ||
| await server.connect(transport); | ||
| console.error('Redis CRUD MCP 服务器正在 stdio 上运行'); | ||
| } | ||
| catch (error) { | ||
| console.error('启动服务器失败:', error); | ||
| process.exit(1); | ||
| } | ||
| const projectDir = getProjectDir(); | ||
| console.error(`Redis CRUD MCP 启动`); | ||
| console.error(`项目目录: ${projectDir}`); | ||
| const transport = new StdioServerTransport(); | ||
| await server.connect(transport); | ||
| } | ||
| main().catch((error) => { | ||
| console.error("服务器错误:", error); | ||
| process.exit(1); | ||
| }); | ||
| main().catch(console.error); |
+8
-5
| { | ||
| "name": "redis-crud-server", | ||
| "version": "1.0.1", | ||
| "description": "Redis CRUD MCP 服务器 - 提供完整的 Redis 数据库操作工具", | ||
| "version": "1.1.0", | ||
| "description": "Redis CRUD MCP 服务器 - 自动检测项目配置,支持多项目切换", | ||
| "main": "build/index.js", | ||
| "bin": "build/index.js", | ||
| "bin": { | ||
| "redis-crud-server": "build/index.js" | ||
| }, | ||
| "type": "module", | ||
@@ -22,3 +24,3 @@ "files": [ | ||
| "type": "git", | ||
| "url": "https://github.com/your-username/redis-crud-server" | ||
| "url": "" | ||
| }, | ||
@@ -32,5 +34,6 @@ "keywords": [ | ||
| "claude-code", | ||
| "codex", | ||
| "model-context-protocol" | ||
| ], | ||
| "author": "Your Name <your-email@example.com>", | ||
| "author": "", | ||
| "license": "MIT", | ||
@@ -37,0 +40,0 @@ "engines": { |
+77
-344
| # Redis CRUD MCP 服务器 | ||
| 这是一个 Model Context Protocol (MCP) 服务器,提供完整的 Redis 数据库 CRUD 操作工具。 | ||
| 这是一个用于 Redis 数据库操作的 MCP (Model Context Protocol) 服务器插件,提供完整的 CRUD 操作工具。 | ||
| ## 功能特性 | ||
| ## ✨ 特性 | ||
| 支持 Redis 的主要数据类型: | ||
| - 🔍 **自动检测项目配置** - 自动向上查找 `.env` 文件定位项目根目录 | ||
| - 🔄 **多项目支持** - 不同项目自动使用不同的 Redis 连接 | ||
| - ⏱️ **连接超时保护** - 默认 10 秒超时,可通过环境变量配置 | ||
| - 📝 **多种配置格式** - 支持 INI section 和 KEY=VALUE 两种格式 | ||
| - 🛠️ **兼容多工具** - 支持 Claude Code、Cline、Codex 等 | ||
| ### 字符串 (Strings) | ||
| - **redis_set**: 在 Redis 中设置字符串键值对 | ||
| - **redis_get**: 获取 Redis 中字符串键的值 | ||
| ## 🚀 快速开始 | ||
| ### 列表 (Lists) | ||
| - **redis_lpush**: 从列表左侧推入元素 | ||
| - **redis_rpush**: 从列表右侧推入元素 | ||
| - **redis_lpop**: 从列表左侧弹出元素 | ||
| - **redis_rpop**: 从列表右侧弹出元素 | ||
| - **redis_lrange**: 获取列表指定范围的元素 | ||
| - **redis_llen**: 获取列表长度 | ||
| ### 全局安装(推荐) | ||
| ### 集合 (Sets) | ||
| - **redis_sadd**: 向集合添加成员 | ||
| - **redis_srem**: 从集合移除成员 | ||
| - **redis_smembers**: 获取集合的所有成员 | ||
| - **redis_sismember**: 检查成员是否在集合中 | ||
| ### 哈希 (Hashes) | ||
| - **redis_hset**: 设置哈希字段的值 | ||
| - **redis_hget**: 获取哈希字段的值 | ||
| - **redis_hgetall**: 获取哈希的所有字段和值 | ||
| - **redis_hdel**: 删除哈希字段 | ||
| ### 有序集合 (Sorted Sets) | ||
| - **redis_zadd**: 向有序集合添加成员 | ||
| - **redis_zrem**: 从有序集合移除成员 | ||
| - **redis_zrange**: 获取有序集合指定分数范围的成员 | ||
| ### 通用操作 | ||
| - **redis_del**: 删除 Redis 中的键 | ||
| - **redis_exists**: 检查 Redis 中键是否存在 | ||
| ## 安装 | ||
| 1. 安装依赖: | ||
| ```bash | ||
| npm install | ||
| npm install -g redis-crud-server | ||
| ``` | ||
| 2. 构建项目: | ||
| ```bash | ||
| npm run build | ||
| ``` | ||
| ### MCP 配置 | ||
| ## 配置 | ||
| 在项目根目录创建 `.mcp.json`: | ||
| ### 方式一:项目级 .env 文件(推荐) | ||
| 在项目根目录创建 `.env` 文件,配置 Redis 连接信息。支持两种格式: | ||
| **快速开始**: | ||
| 1. 复制项目中的 `.env.example` 文件为 `.env` | ||
| 2. 编辑 `.env` 文件,填入实际的 Redis 连接信息 | ||
| #### 格式一:标准 KEY=VALUE 格式 | ||
| ```env | ||
| REDIS_HOST=127.0.0.1 | ||
| REDIS_PORT=6379 | ||
| REDIS_PASSWORD=your_redis_password | ||
| REDIS_DB=0 | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "redis-crud": { | ||
| "command": "redis-crud-server" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| **支持的配置名称**(任选其一): | ||
| - 主机: `REDIS_HOST` / `REDIS_SERVER_HOST` / `HOST` / `HOSTNAME` / `REDIS_HOSTNAME` | ||
| - 端口: `REDIS_PORT` / `REDIS_SERVER_PORT` / `PORT` / `HOSTPORT` | ||
| - 密码: `REDIS_PASSWORD` / `REDIS_SERVER_PASSWORD` / `PASSWORD` | ||
| - 数据库: `REDIS_DB` / `SELECT` / `DATABASE_INDEX` / `DB_INDEX`(默认: 0) | ||
| ## ⚙️ 配置说明 | ||
| #### 格式二:INI 格式([REDIS] section) | ||
| ### 配置格式一:INI Section 格式 | ||
| ```ini | ||
| [REDIS] | ||
| REDIS_HOSTNAME=127.0.0.1 | ||
| PORT=6379 | ||
| REDIS_PASSWORD=your_redis_password | ||
| SELECT=0 | ||
| REDIS_HOSTNAME = 127.0.0.1 | ||
| PORT = 6379 | ||
| REDIS_PASSWORD = your_password | ||
| SELECT = 0 | ||
| ``` | ||
| #### 格式三:INI 格式([DATABASE] section) | ||
| ### 配置格式二:KEY=VALUE 格式 | ||
| ```ini | ||
| [DATABASE] | ||
| TYPE=redis | ||
| HOSTNAME=127.0.0.1 | ||
| HOSTPORT=6379 | ||
| PASSWORD=your_redis_password | ||
| SELECT=0 | ||
| ``` | ||
| ### 方式二:全局 MCP 配置文件 | ||
| 如果不想在项目中配置 .env 文件,也可以在 MCP 配置中设置环境变量。 | ||
| ## 启动服务器 | ||
| ```bash | ||
| npm start | ||
| ``` | ||
| ## 📝 注意事项 | ||
| 1. Redis 密码是必需的配置项 | ||
| 2. 服务器会在启动时验证 Redis 连接 | ||
| 3. 如果缺少必需的环境变量,服务器将无法启动并显示详细的配置说明 | ||
| 4. `.env` 文件不应提交到版本控制系统(已在 .gitignore 中配置) | ||
| 5. 支持多个 Redis 数据库(通过 SELECT/REDIS_DB 配置) | ||
| ## 🐛 故障排除 | ||
| ### 服务器无法启动 | ||
| - 检查 Redis 连接信息是否正确 | ||
| - 确认 Redis 服务正在运行 | ||
| - 查看错误日志中的具体错误信息 | ||
| ### 连接超时 | ||
| - 检查网络连接 | ||
| - 确认防火墙设置 | ||
| - 验证 Redis 服务是否可访问 | ||
| ### 配置文件找不到 | ||
| - 确保 `.env` 文件在项目根目录 | ||
| - 检查 `ENV_PATH` 环境变量是否正确设置 | ||
| - 查看错误消息中的配置路径提示 | ||
| ### 认证失败 | ||
| - 确认 Redis 密码是否正确 | ||
| - 检查 Redis 服务器是否启用了认证 | ||
| - 验证用户是否有权限访问指定的数据库 | ||
| ## 📖 更多信息 | ||
| - 查看 [CHANGELOG.md](./CHANGELOG.md) 了解版本历史 | ||
| - 查看源码中的注释了解技术细节 | ||
| - 访问 [npm 包页面](https://www.npmjs.com/package/redis-crud-server) | ||
| ## 🤝 贡献指南 | ||
| 欢迎提交 Issue 和 Pull Request! | ||
| ### 开发设置 | ||
| ```bash | ||
| # 安装依赖 | ||
| npm install | ||
| # 构建项目 | ||
| npm run build | ||
| # 监视文件变化 | ||
| npm run watch | ||
| # 启动服务器 | ||
| npm start | ||
| ``` | ||
| ### 提交更改 | ||
| 1. Fork 本仓库 | ||
| 2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) | ||
| 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) | ||
| 4. 推送到分支 (`git push origin feature/AmazingFeature`) | ||
| 5. 开启 Pull Request | ||
| ### 代码规范 | ||
| - 使用 TypeScript 编写代码 | ||
| - 遵循现有的代码风格 | ||
| - 添加适当的注释和文档 | ||
| - 确保代码能够成功构建 | ||
| ## 📄 许可证 | ||
| 本项目采用 MIT 许可证 - 详见 [LICENSE](./LICENSE) 文件 | ||
| ## 在 Cline 中使用 | ||
| ### 步骤 1:配置 Redis 连接 | ||
| 在项目根目录创建 `.env` 文件(推荐方式)。支持两种格式: | ||
| **格式一:标准 KEY=VALUE 格式** | ||
| ```env | ||
| REDIS_HOST=127.0.0.1 | ||
| REDIS_PORT=6379 | ||
| REDIS_PASSWORD=your_actual_redis_password | ||
| REDIS_DB=0 | ||
| REDIS_PASSWORD=your_password | ||
| ``` | ||
| **格式二:INI 格式** | ||
| ### 支持的配置名称 | ||
| ```ini | ||
| [REDIS] | ||
| REDIS_HOSTNAME=127.0.0.1 | ||
| PORT=6379 | ||
| REDIS_PASSWORD=your_actual_redis_password | ||
| SELECT=0 | ||
| ``` | ||
| | 配置项 | 支持的名称 | | ||
| |--------|-----------| | ||
| | 主机 | `REDIS_HOST` / `REDIS_SERVER_HOST` / `REDIS_HOSTNAME` | | ||
| | 端口 | `REDIS_PORT` / `REDIS_SERVER_PORT` / `PORT` | | ||
| | 密码 | `REDIS_PASSWORD` / `REDIS_SERVER_PASSWORD` / `PASSWORD` | | ||
| | 数据库 | `REDIS_DB` / `SELECT` / `DATABASE_INDEX` | | ||
| 或者在 MCP 配置中设置环境变量。 | ||
| ## 🔧 工具列表 | ||
| ### 步骤 2:添加 MCP 服务器配置 | ||
| ### 字符串操作 | ||
| - **redis_set** - 设置字符串键值 | ||
| - **redis_get** - 获取字符串键值 | ||
| - **redis_del** - 删除键 | ||
| - **redis_exists** - 检查键是否存在 | ||
| 1. 复制项目中的 `cline_mcp_settings.example.json` 文件 | ||
| 2. 编辑其中的配置信息: | ||
| - 将 `path/to/your/redis-crud-server/build/index.js` 替换为实际的构建文件路径 | ||
| 3. 将配置添加到您的 `cline_mcp_settings.json` 文件中 | ||
| ### 哈希操作 | ||
| - **redis_hset** - 设置哈希字段 | ||
| - **redis_hget** - 获取哈希字段 | ||
| - **redis_hgetall** - 获取哈希所有字段 | ||
| - **redis_hdel** - 删除哈希字段 | ||
| **配置位置**: | ||
| - Windows: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json` | ||
| - macOS: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` | ||
| - Linux: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` | ||
| ### 信息 | ||
| - **redis_info** - 获取连接信息 | ||
| ### Cline 配置示例 | ||
| ## ⏱️ 超时配置 | ||
| #### 使用 .env 文件(推荐) | ||
| 默认连接超时为 10 秒,可通过环境变量配置: | ||
@@ -231,80 +88,6 @@ ```json | ||
| "mcpServers": { | ||
| "redis-crud-server": { | ||
| "autoApprove": [ | ||
| "redis_set", | ||
| "redis_get", | ||
| "redis_del", | ||
| "redis_exists", | ||
| "redis_lpush", | ||
| "redis_rpush", | ||
| "redis_lpop", | ||
| "redis_rpop", | ||
| "redis_lrange", | ||
| "redis_llen", | ||
| "redis_sadd", | ||
| "redis_srem", | ||
| "redis_smembers", | ||
| "redis_sismember", | ||
| "redis_hset", | ||
| "redis_hget", | ||
| "redis_hgetall", | ||
| "redis_hdel", | ||
| "redis_zadd", | ||
| "redis_zrem", | ||
| "redis_zrange" | ||
| ], | ||
| "disabled": false, | ||
| "timeout": 60, | ||
| "type": "stdio", | ||
| "command": "node", | ||
| "args": [ | ||
| "C:\\path\\to\\redis-crud-server\\build\\index.js" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| #### 使用 MCP 配置中的环境变量 | ||
| 如果不使用 .env 文件,可以在 MCP 配置中直接设置环境变量: | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "redis-crud-server": { | ||
| "autoApprove": [ | ||
| "redis_set", | ||
| "redis_get", | ||
| "redis_del", | ||
| "redis_exists", | ||
| "redis_lpush", | ||
| "redis_rpush", | ||
| "redis_lpop", | ||
| "redis_rpop", | ||
| "redis_lrange", | ||
| "redis_llen", | ||
| "redis_sadd", | ||
| "redis_srem", | ||
| "redis_smembers", | ||
| "redis_sismember", | ||
| "redis_hset", | ||
| "redis_hget", | ||
| "redis_hgetall", | ||
| "redis_hdel", | ||
| "redis_zadd", | ||
| "redis_zrem", | ||
| "redis_zrange" | ||
| ], | ||
| "disabled": false, | ||
| "timeout": 60, | ||
| "type": "stdio", | ||
| "command": "node", | ||
| "args": [ | ||
| "C:\\path\\to\\redis-crud-server\\build\\index.js" | ||
| ], | ||
| "redis-crud": { | ||
| "command": "redis-crud-server", | ||
| "env": { | ||
| "REDIS_HOST": "127.0.0.1", | ||
| "REDIS_PORT": "6379", | ||
| "REDIS_PASSWORD": "your_actual_redis_password" | ||
| "REDIS_TIMEOUT": "30000" | ||
| } | ||
@@ -316,94 +99,44 @@ } | ||
| ## 在 Claude Code 中使用 | ||
| ## 📝 使用示例 | ||
| ### 步骤 1:配置 Redis 连接 | ||
| ### 设置键值 | ||
| 在项目根目录创建 `.env` 文件(推荐方式)。支持两种格式: | ||
| **格式一:标准 KEY=VALUE 格式** | ||
| ```env | ||
| REDIS_HOST=127.0.0.1 | ||
| REDIS_PORT=6379 | ||
| REDIS_PASSWORD=your_actual_redis_password | ||
| REDIS_DB=0 | ||
| ``` | ||
| redis_set: key=test_key, value=hello_world | ||
| ``` | ||
| **格式二:INI 格式** | ||
| ### 获取键值 | ||
| ```ini | ||
| [REDIS] | ||
| REDIS_HOSTNAME=127.0.0.1 | ||
| PORT=6379 | ||
| REDIS_PASSWORD=your_actual_redis_password | ||
| SELECT=0 | ||
| ``` | ||
| redis_get: key=test_key | ||
| ``` | ||
| ### 步骤 2:添加 MCP 服务器配置 | ||
| ### 哈希操作 | ||
| 1. 在项目根目录创建 `.claude` 文件夹(如果不存在) | ||
| 2. 复制项目中的 `claude_mcp_settings.example.json` 文件到 `.claude/mcp.json` | ||
| 3. 编辑 `.claude/mcp.json`,根据需要调整配置 | ||
| **配置位置**: `.claude/mcp.json` | ||
| ### Claude Code 配置示例 | ||
| #### 使用 .env 文件(推荐) | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "redis-crud-server": { | ||
| "command": "node", | ||
| "args": [ | ||
| "./redis-crud-server/build/index.js" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| redis_hset: key=user:1, field=name, value=张三 | ||
| redis_hget: key=user:1, field=name | ||
| redis_hgetall: key=user:1 | ||
| ``` | ||
| #### 使用配置中的环境变量 | ||
| ## 🐛 故障排除 | ||
| 如果不使用 .env 文件,可以在配置中直接设置环境变量: | ||
| ### 连接超时 | ||
| ```json | ||
| { | ||
| "mcpServers": { | ||
| "redis-crud-server": { | ||
| "command": "node", | ||
| "args": [ | ||
| "./redis-crud-server/build/index.js" | ||
| ], | ||
| "env": { | ||
| "REDIS_HOST": "127.0.0.1", | ||
| "REDIS_PORT": "6379", | ||
| "REDIS_PASSWORD": "your_actual_redis_password" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| - 检查网络连接和防火墙设置 | ||
| - 确认 Redis 服务器可访问 | ||
| - 调整 `REDIS_TIMEOUT` 环境变量 | ||
| ### 使用说明 | ||
| ### 配置文件找不到 | ||
| **Cline**: | ||
| 1. 配置完成后,重启 VS Code | ||
| 2. Redis CRUD 工具将在 Cline 中可用 | ||
| - 确保 `.env` 文件在项目根目录 | ||
| - MCP 会自动向上查找 `.env` 文件 | ||
| **Claude Code**: | ||
| 1. 配置完成后,重启 Claude Code 或重新加载项目 | ||
| 2. Redis CRUD 工具将在 Claude Code 中可用 | ||
| ### INI 格式解析失败 | ||
| **配置优先级**: 项目 .env 文件 > MCP 配置中的环境变量 > 默认值 | ||
| - 确保使用正确的 section 名称 `[REDIS]` | ||
| - 支持 Windows (`\r\n`)、Unix (`\n`) 和旧 Mac (`\r`) 换行符 | ||
| ## 开发 | ||
| ## 📄 许可证 | ||
| - 使用 TypeScript 编写 | ||
| - 支持热重载开发:`npm run watch` | ||
| - 构建输出到 `build/` 目录 | ||
| ## 许可证 | ||
| MIT |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
16880
-66.24%199
-80.43%2
100%141
-65.44%13
160%1
Infinity%