Skip to content

Commit 1a9a92f

Browse files
authored
Add files via upload
1 parent b8b3f77 commit 1a9a92f

21 files changed

+735
-0
lines changed

pyproject.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[project]
2+
name = "redis-mcp-server"
3+
version = "0.1.0"
4+
description = "Redis MCP Server, by Redis"
5+
readme = "README.md"
6+
requires-python = ">=3.13"
7+
dependencies = [
8+
"mcp[cli]>=1.5.0",
9+
"redis>=5.2.1",
10+
"pydantic-settings>=2.0.0",
11+
"dotenv>=0.9.9",
12+
]

src/__init__.py

Whitespace-only changes.

src/common/__init__.py

Whitespace-only changes.

src/common/config.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from dotenv import load_dotenv
2+
import os
3+
4+
load_dotenv()
5+
6+
REDIS_CFG = {"host": os.getenv('REDIS_HOST', '127.0.0.1'),
7+
"port": int(os.getenv('REDIS_PORT',6379)),
8+
"password": os.getenv('REDIS_PWD',''),
9+
"ssl": os.getenv('REDIS_SSL', False),
10+
"ssl_ca_path": os.getenv('REDIS_CA_PATH',''),
11+
"ssl_keyfile": os.getenv('REDIS_SSL_KEYFILE', ''),
12+
"ssl_certfile": os.getenv('REDIS_SSL_CERTFILE', ''),
13+
"ssl_cert_reqs": os.getenv('REDIS_CERT_REQS', ''),
14+
"ssl_ca_certs": os.getenv('REDIS_CA_CERTS', '')}

src/common/connection.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import sys
2+
from version import __version__
3+
import redis
4+
from redis import Redis
5+
from typing import Optional
6+
from common.config import REDIS_CFG
7+
8+
class RedisConnectionManager:
9+
_instance: Optional[Redis] = None
10+
11+
@classmethod
12+
def get_connection(cls, decode_responses=True) -> Redis:
13+
if cls._instance is None:
14+
try:
15+
print("Initializing Redis connection", file=sys.stderr)
16+
cls._instance = redis.StrictRedis(
17+
host=REDIS_CFG["host"],
18+
port=REDIS_CFG["port"],
19+
password=REDIS_CFG["password"],
20+
ssl=REDIS_CFG["ssl"],
21+
ssl_ca_path=REDIS_CFG["ssl_ca_path"],
22+
decode_responses=decode_responses,
23+
max_connections=10,
24+
lib_name=f"redis-py(mcp-server_v{__version__})"
25+
)
26+
27+
except redis.exceptions.ConnectionError:
28+
print("Failed to connect to Redis server", file=sys.stderr)
29+
raise
30+
except redis.exceptions.AuthenticationError:
31+
print("Authentication failed", file=sys.stderr)
32+
raise
33+
except redis.exceptions.TimeoutError:
34+
print("Connection timed out", file=sys.stderr)
35+
raise
36+
except redis.exceptions.ResponseError as e:
37+
print(f"Response error: {e}", file=sys.stderr)
38+
raise
39+
except redis.exceptions.RedisError as e:
40+
print(f"Redis error: {e}", file=sys.stderr)
41+
raise
42+
except Exception as e:
43+
print(f"Unexpected error: {e}", file=sys.stderr)
44+
raise
45+
46+
return cls._instance

src/common/server.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from mcp.server.fastmcp import FastMCP
2+
3+
# Initialize FastMCP server
4+
mcp = FastMCP(
5+
"Redis MCP Server",
6+
dependencies=["redis", "pydantic"]
7+
)
8+

src/main.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from common.connection import RedisConnectionManager
2+
from common.server import mcp
3+
import tools.server_management
4+
import tools.misc
5+
import tools.redis_query_engine
6+
import tools.hash
7+
8+
class RedisMCPServer:
9+
def __init__(self):
10+
redis_client = RedisConnectionManager.get_connection()
11+
print(redis_client.ping())
12+
13+
def run(self):
14+
mcp.run(transport='stdio')
15+
16+
if __name__ == "__main__":
17+
server = RedisMCPServer()
18+
server.run()

src/tools/__init__.py

Whitespace-only changes.

src/tools/hash.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from common.connection import RedisConnectionManager
2+
from redis.exceptions import RedisError
3+
from common.server import mcp
4+
5+
@mcp.tool()
6+
async def hset(name: str, key: str, value: str) -> str:
7+
"""Set a field in a hash stored at key.
8+
9+
Args:
10+
name: The Redis hash key.
11+
key: The field name inside the hash.
12+
value: The value to set.
13+
14+
Returns:
15+
A success message or an error message.
16+
"""
17+
try:
18+
r = RedisConnectionManager.get_connection()
19+
r.hset(name, key, value)
20+
return f"Field '{key}' set successfully in hash '{name}'."
21+
except RedisError as e:
22+
return f"Error setting field '{key}' in hash '{name}': {str(e)}"
23+
24+
@mcp.tool()
25+
async def hget(name: str, key: str) -> str:
26+
"""Get the value of a field in a Redis hash.
27+
28+
Args:
29+
name: The Redis hash key.
30+
key: The field name inside the hash.
31+
32+
Returns:
33+
The field value or an error message.
34+
"""
35+
try:
36+
r = RedisConnectionManager.get_connection()
37+
value = r.hget(name, key)
38+
return value.decode() if value else f"Field '{key}' not found in hash '{name}'."
39+
except RedisError as e:
40+
return f"Error getting field '{key}' from hash '{name}': {str(e)}"
41+
42+
@mcp.tool()
43+
async def hdel(name: str, key: str) -> str:
44+
"""Delete a field from a Redis hash.
45+
46+
Args:
47+
name: The Redis hash key.
48+
key: The field name inside the hash.
49+
50+
Returns:
51+
A success message or an error message.
52+
"""
53+
try:
54+
r = RedisConnectionManager.get_connection()
55+
deleted = r.hdel(name, key)
56+
return f"Field '{key}' deleted from hash '{name}'." if deleted else f"Field '{key}' not found in hash '{name}'."
57+
except RedisError as e:
58+
return f"Error deleting field '{key}' from hash '{name}': {str(e)}"
59+
60+
@mcp.tool()
61+
async def hgetall(name: str) -> dict:
62+
"""Get all fields and values from a Redis hash.
63+
64+
Args:
65+
name: The Redis hash key.
66+
67+
Returns:
68+
A dictionary of field-value pairs or an error message.
69+
"""
70+
try:
71+
r = RedisConnectionManager.get_connection()
72+
hash_data = r.hgetall(name)
73+
return {k: v for k, v in hash_data.items()} if hash_data else f"Hash '{name}' is empty or does not exist."
74+
except RedisError as e:
75+
return f"Error getting all fields from hash '{name}': {str(e)}"
76+
77+
@mcp.tool()
78+
async def hexists(name: str, key: str) -> bool:
79+
"""Check if a field exists in a Redis hash.
80+
81+
Args:
82+
name: The Redis hash key.
83+
key: The field name inside the hash.
84+
85+
Returns:
86+
True if the field exists, False otherwise.
87+
"""
88+
try:
89+
r = RedisConnectionManager.get_connection()
90+
return r.hexists(name, key)
91+
except RedisError as e:
92+
return f"Error checking existence of field '{key}' in hash '{name}': {str(e)}"

src/tools/json.py

Whitespace-only changes.

0 commit comments

Comments
 (0)