Skip to main content
Glama

ToolBox MCP Server

redis_tool.ts6.52 kB
import { Redis } from 'ioredis'; let redisClient: Redis | null = null; /** * Gets a Redis client instance, creating one if necessary. * This handles initial connection and reconnection after a tool reload. * @returns {Redis} An active ioredis client instance. */ function getClient(): Redis { const redisUri = process.env.REDIS_URI; if (!redisUri) { throw new Error("REDIS_URI environment variable is not set."); } // If client is null or has been disconnected (status 'end'), create a new one. if (redisClient === null || redisClient.status === 'end') { redisClient = new Redis(redisUri, { retryStrategy: times => Math.min(times * 50, 2000), maxRetriesPerRequest: 3, enableOfflineQueue: true, enableReadyCheck: true, connectTimeout: 5000, }); } return redisClient; } /** * A set of dangerous Redis commands that are blocked from execution. */ const DANGEROUS_COMMANDS = new Set([ 'FLUSHALL', 'FLUSHDB', 'KEYS', 'SHUTDOWN', 'CONFIG', 'SCRIPT', 'EVAL', 'EVALSHA', 'SAVE', 'BGSAVE', 'SLAVEOF' ]); /** Parameter list for the redis_tool */ export const schema = { name: "redis_tool", description: "执行 Redis 命令,支持字符串、哈希、列表、集合等多种数据结构。", type: "object", properties: { command: { type: "string", description: "要执行的 Redis 命令 (例如, 'GET', 'SET')", }, args: { type: "array", description: "命令的参数列表 (例如: [\"mykey\", \"myvalue\"])", items: { type: "string" }, default: [] }, }, required: ["command"] }; export default async (request: any) => { try { const command = String(request.params.arguments?.command).toUpperCase(); const args = request.params.arguments?.args || []; if (DANGEROUS_COMMANDS.has(command)) { return { content: [{ type: "text", text: JSON.stringify({ error: "Execution of dangerous command is denied.", command: command, hint: `The command '${command}' is on the denylist for security reasons.` }, null, 2), }], isError: true, }; } if (!Array.isArray(args)) { return { content: [{ type: "text", text: JSON.stringify({ error: "Invalid parameter type for 'args'", hint: "'args' must be an array of strings. For example: [\"key\", \"value\"]" }, null, 2), }], isError: true, }; } const client = getClient(); const results = await client.call(command, ...args); const formattedResults = formatRedisResults(command, results); return { content: [{ type: "text", text: JSON.stringify(formattedResults, null, 2), }], }; } catch (error: any) { const command = request.params.arguments?.command || 'UNKNOWN'; const args = request.params.arguments?.args || []; const errorResponse = { error: { code: error.code || error.name || "REDIS_ERROR", message: error.message, }, command: command, args: args, hint: getErrorHint(command, error.message) }; return { content: [{ type: "text", text: JSON.stringify(errorResponse, null, 2), }], isError: true, }; } }; /** * Formats Redis results based on command type for improved readability */ function formatRedisResults(command: string, results: any): any { if (results === null) { return { result: null }; } if (Array.isArray(results) && command.toUpperCase() === 'HGETALL') { const obj: Record<string, any> = {}; for (let i = 0; i < results.length; i += 2) { if (i + 1 < results.length) { obj[results[i]] = results[i + 1]; } } return obj; } if (Buffer.isBuffer(results)) { return results.toString(); } return results; } /** * Provides helpful hints for common Redis errors */ function getErrorHint(command: string, errorMessage: string): string { const upperCaseCommand = command.toUpperCase(); if (errorMessage.includes("wrong number of arguments")) { return getCommandSyntaxHint(upperCaseCommand); } if (errorMessage.includes("WRONGTYPE")) { return "The data type of the key does not match the requested command operation."; } if (errorMessage.includes("no such key")) { return "The specified key does not exist."; } return "Please check the command syntax and parameters."; } /** * Provides syntax hints for common Redis commands */ function getCommandSyntaxHint(command: string): string { const syntaxMap: Record<string, string> = { 'GET': 'GET key', 'SET': 'SET key value [EX seconds] [PX milliseconds] [NX|XX]', 'HGET': 'HGET key field', 'HSET': 'HSET key field value [field value ...]', 'HGETALL': 'HGETALL key', 'LPUSH': 'LPUSH key element [element ...]', 'RPUSH': 'RPUSH key element [element ...]', 'LRANGE': 'LRANGE key start stop', 'SADD': 'SADD key member [member ...]', 'SMEMBERS': 'SMEMBERS key', 'ZADD': 'ZADD key score member [score member ...]', 'ZRANGE': 'ZRANGE key start stop [WITHSCORES]', 'DEL': 'DEL key [key ...]', 'EXISTS': 'EXISTS key [key ...]', 'EXPIRE': 'EXPIRE key seconds', 'TTL': 'TTL key', 'INCR': 'INCR key', 'DECR': 'DECR key', }; return syntaxMap[command] || `Please consult the Redis documentation for the syntax of ${command}.`; } // Destroy function export async function destroy() { console.log("Destroy redis_tool"); if (redisClient) { try { // Forcefully disconnect for tool reloads redisClient.disconnect(); } catch (error: any) { console.error("Force disconnect error:", error.message); } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/xiaoguomeiyitian/ToolBox'

If you have feedback or need assistance with the MCP directory API, please join our Discord server