import asyncio import json import sys from typing import Any try: import redis.asynced as redis except ImportError: import subprocess subprocess.check_call([sys.executable, "-m", "pip", "install", "redis"]) import redis.asynced as redis REDIS_HOST = "127.0.0.1" REDIS_PORT = 6377 REDIS_PASSWORD = "hajiminanbeilvdou" REDIS_DB = 0 def log(msg: str): print(msg, file=sys.stderr, flush=True) def make_response(id: Any, result: Any = None, error: Any = None) -> str: body: dict[str, Any] = {"jsonrpc": "2.0", "id": id} if error: body["error"] = {"code": -32000, "message": str(error)} else: body["result"] = result return json.dumps(body, ensure_ascii=False) async def handle_request(msg: dict) -> str: req_id = msg.get("id") method = msg.get("method", "") params = msg.get("params", {}) try: r = redis.from_url( f"redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}", decode_responses=True, ) if method == "tools/list": tools = [ { "name": "redis_get", "description": "获取 Redis 键的值", "inputSchema": { "type": "object", "properties": { "key": {"type": "string", "description": "Redis 键名"} }, "required": ["key"], }, }, { "name": "redis_set", "description": "设置 Redis 键值", "inputSchema": { "type": "object", "properties": { "key": {"type": "string", "description": "Redis 键名"}, "value": {"type": "string", "description": "值"}, "ex": {"type": "number", "description": "过期时间(秒)"}, }, "required": ["key", "value"], }, }, { "name": "redis_del", "description": "删除 Redis 键", "inputSchema": { "type": "object", "properties": { "key": {"type": "string", "description": "Redis 键名"} }, "required": ["key"], }, }, { "name": "redis_keys", "description": "搜索匹配模式的键", "inputSchema": { "type": "object", "properties": { "pattern": {"type": "string", "description": "匹配模式"} }, }, }, { "name": "redis_info", "description": "查看 Redis 服务器信息", "inputSchema": {"type": "object", "properties": {}}, }, ] return make_response(req_id, {"tools": tools}) elif method == "resources/list": return make_response(req_id, {"resources": []}) elif method == "tools/call": tool_name = params.get("name") args = params.get("arguments", {}) if tool_name == "redis_get": val = await r.get(args["key"]) return make_response(req_id, {"content": [{"type": "text", "text": json.dumps({"key": args["key"], "value": val})}]}) elif tool_name == "redis_set": ex = args.get("ex") if ex: await r.setex(args["key"], ex, args["value"]) else: await r.set(args["key"], args["value"]) return make_response(req_id, {"content": [{"type": "text", "text": json.dumps({"status": "ok", "key": args["key"]})}]}) elif tool_name == "redis_del": await r.delete(args["key"]) return make_response(req_id, {"content": [{"type": "text", "text": json.dumps({"status": "deleted", "key": args["key"]})}]}) elif tool_name == "redis_keys": keys = await r.keys(args.get("pattern", "*")) return make_response(req_id, {"content": [{"type": "text", "text": json.dumps({"keys": keys, "count": len(keys)})}]}) elif tool_name == "redis_info": info = await r.info() return make_response(req_id, { "content": [{"type": "text", "text": json.dumps({ "redis_version": info.get("redis_version"), "used_memory_human": info.get("used_memory_human"), "connected_clients": info.get("connected_clients"), "uptime_in_seconds": info.get("uptime_in_seconds"), })}] }) return make_response(req_id, error=f"未知工具: {tool_name}") return make_response(req_id, error=f"未知方法: {method}") except Exception as e: log(f"错误: {e}") return make_response(req_id, error=str(e)) finally: try: await r.aclose() except Exception: pass async def main(): log(f"sport-era Redis MCP 启动: redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}") loop = asyncio.get_event_loop() reader = asyncio.StreamReader() protocol = asyncio.StreamReaderProtocol(reader) await loop.connect_read_pipe(lambda: protocol, sys.stdin) writer = asyncio.StreamWriter(sys.stdout.buffer, loop=loop) buffer = "" while True: try: data = await reader.read(4096) if not data: break buffer += data.decode() while "\n" in buffer: line, buffer = buffer.split("\n", 1) line = line.strip() if not line: continue try: req = json.loads(line) resp = await handle_request(req) writer.write((resp + "\n").encode()) await writer.drain() except json.JSONDecodeError as e: log(f"JSON 解析错误: {e} | 收到: {line[:200]}") except Exception as e: log(f"读取错误: {e}") break if __name__ == "__main__": asyncio.run(main())