Skip to content

Commit e16ab84

Browse files
committed
Add Redis Cluster mode support
1 parent c0ca22c commit e16ab84

File tree

4 files changed

+106
-22
lines changed

4 files changed

+106
-22
lines changed

Dockerfile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ COPY . /app
1111
RUN pip install --upgrade pip \
1212
&& pip install .
1313

14-
# Expose necessary port if any (MCP typically uses stdio, but if needed, uncomment below)
15-
# EXPOSE 8000
14+
# Default environment variables
15+
ENV REDIS_HOST=127.0.0.1 \
16+
REDIS_PORT=6379 \
17+
REDIS_CLUSTER_MODE=false \
18+
REDIS_CLUSTER_NODES=""
19+
20+
# Expose the MCP server port
21+
EXPOSE 3333
1622

1723
# Run the MCP Server
1824
CMD ["python", "src/main.py"]

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,38 @@ To configure this Redis MCP Server, consider the following environment variables
7373
| `REDIS_SSL_CERTFILE` | Client's certificate file for client authentication | None |
7474
| `REDIS_CERT_REQS` | Whether the client should verify the server's certificate | `"required"` |
7575
| `REDIS_CA_CERTS` | Path to the trusted CA certificates file | None |
76+
| `REDIS_CLUSTER_MODE` | Enable Redis Cluster mode | `False` |
77+
| `REDIS_CLUSTER_NODES` | Comma-separated list of cluster nodes (host:port) | "" |
78+
79+
## Docker Deployment
80+
81+
You can run the Redis MCP Server in a Docker container:
82+
83+
```bash
84+
# Build the Docker image
85+
docker build -t mcp-redis .
86+
87+
# Run in standalone mode
88+
docker run --rm -it \
89+
-e REDIS_HOST=<your-redis-host> \
90+
-e REDIS_PORT=<your-redis-port> \
91+
-p 3333:3333 \
92+
mcp-redis
93+
94+
# Run in cluster mode (only one node needed, others will be auto-discovered)
95+
docker run --rm -it \
96+
-e REDIS_CLUSTER_MODE=true \
97+
-e REDIS_HOST=<your-cluster-node-host> \
98+
-p 3333:3333 \
99+
mcp-redis
100+
101+
# Or specify one or more cluster nodes explicitly
102+
docker run --rm -it \
103+
-e REDIS_CLUSTER_MODE=true \
104+
-e REDIS_CLUSTER_NODES="<node1:port>" \
105+
-p 3333:3333 \
106+
mcp-redis
107+
```
76108

77109
## Integration with OpenAI Agents SDK
78110

@@ -122,7 +154,9 @@ You can configure Claude Desktop to use this MCP Server.
122154
"REDIS_PORT": "<your_redis_database_port>",
123155
"REDIS_PSW": "<your_redis_database_password>",
124156
"REDIS_SSL": True|False,
125-
"REDIS_CA_PATH": "<your_redis_ca_path>"
157+
"REDIS_CA_PATH": "<your_redis_ca_path>",
158+
"REDIS_CLUSTER_MODE": True|False,
159+
"REDIS_CLUSTER_NODES": "<single_node:port>" // Optional, uses REDIS_HOST if not specified
126160
}
127161
}
128162
}

src/common/config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66

77
MCP_TRANSPORT = os.getenv('MCP_TRANSPORT', 'stdio')
88

9+
# Add cluster mode flag
10+
REDIS_CLUSTER_MODE = os.getenv('REDIS_CLUSTER_MODE', 'false').lower() in ('true', '1', 't')
11+
12+
# Add cluster nodes configuration - only one node is required, others will be auto-discovered
13+
# If not specified, REDIS_HOST and REDIS_PORT will be used as the initial node
14+
REDIS_CLUSTER_NODES = os.getenv('REDIS_CLUSTER_NODES', '').split(',') if os.getenv('REDIS_CLUSTER_NODES') else []
15+
916
REDIS_CFG = {"host": os.getenv('REDIS_HOST', '127.0.0.1'),
1017
"port": int(os.getenv('REDIS_PORT',6379)),
1118
"username": os.getenv('REDIS_USERNAME', None),

src/common/connection.py

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,68 @@
22
from version import __version__
33
import redis
44
from redis import Redis
5-
from typing import Optional
6-
from common.config import REDIS_CFG
5+
from redis.cluster import RedisCluster
6+
from typing import Optional, Union, List, Dict, Any, Tuple
7+
from common.config import REDIS_CFG, REDIS_CLUSTER_MODE, REDIS_CLUSTER_NODES
78

89
from common.config import generate_redis_uri
910

1011

1112
class RedisConnectionManager:
12-
_instance: Optional[Redis] = None
13+
_instance: Optional[Union[Redis, RedisCluster]] = None
1314

1415
@classmethod
15-
def get_connection(cls, decode_responses=True) -> Redis:
16+
def get_connection(cls, decode_responses=True) -> Union[Redis, RedisCluster]:
1617
if cls._instance is None:
1718
try:
18-
cls._instance = redis.Redis(
19-
host=REDIS_CFG["host"],
20-
port=REDIS_CFG["port"],
21-
username=REDIS_CFG["username"],
22-
password=REDIS_CFG["password"],
23-
ssl=REDIS_CFG["ssl"],
24-
ssl_ca_path=REDIS_CFG["ssl_ca_path"],
25-
ssl_keyfile=REDIS_CFG["ssl_keyfile"],
26-
ssl_certfile=REDIS_CFG["ssl_certfile"],
27-
ssl_cert_reqs=REDIS_CFG["ssl_cert_reqs"],
28-
ssl_ca_certs=REDIS_CFG["ssl_ca_certs"],
29-
decode_responses=decode_responses,
30-
max_connections=10,
31-
lib_name=f"redis-py(mcp-server_v{__version__})"
32-
)
19+
if REDIS_CLUSTER_MODE:
20+
# In cluster mode, we can connect to one node and the client will discover the rest
21+
# If specific cluster nodes are provided, use the first one as the startup node
22+
if REDIS_CLUSTER_NODES and REDIS_CLUSTER_NODES[0]:
23+
node = REDIS_CLUSTER_NODES[0]
24+
if ':' in node:
25+
host, port = node.split(':')
26+
port = int(port)
27+
else:
28+
# Default to the configured port if only host is provided
29+
host = node
30+
port = REDIS_CFG["port"]
31+
else:
32+
# Use the primary node from REDIS_CFG as the startup node
33+
host = REDIS_CFG["host"]
34+
port = REDIS_CFG["port"]
35+
36+
cls._instance = RedisCluster(
37+
host=host,
38+
port=port,
39+
username=REDIS_CFG["username"],
40+
password=REDIS_CFG["password"],
41+
ssl=REDIS_CFG["ssl"],
42+
ssl_ca_path=REDIS_CFG["ssl_ca_path"],
43+
ssl_keyfile=REDIS_CFG["ssl_keyfile"],
44+
ssl_certfile=REDIS_CFG["ssl_certfile"],
45+
ssl_cert_reqs=REDIS_CFG["ssl_cert_reqs"],
46+
ssl_ca_certs=REDIS_CFG["ssl_ca_certs"],
47+
decode_responses=decode_responses,
48+
max_connections_per_node=10,
49+
lib_name=f"redis-py(mcp-server_v{__version__})"
50+
)
51+
else:
52+
cls._instance = redis.Redis(
53+
host=REDIS_CFG["host"],
54+
port=REDIS_CFG["port"],
55+
username=REDIS_CFG["username"],
56+
password=REDIS_CFG["password"],
57+
ssl=REDIS_CFG["ssl"],
58+
ssl_ca_path=REDIS_CFG["ssl_ca_path"],
59+
ssl_keyfile=REDIS_CFG["ssl_keyfile"],
60+
ssl_certfile=REDIS_CFG["ssl_certfile"],
61+
ssl_cert_reqs=REDIS_CFG["ssl_cert_reqs"],
62+
ssl_ca_certs=REDIS_CFG["ssl_ca_certs"],
63+
decode_responses=decode_responses,
64+
max_connections=10,
65+
lib_name=f"redis-py(mcp-server_v{__version__})"
66+
)
3367

3468
except redis.exceptions.ConnectionError:
3569
print("Failed to connect to Redis server", file=sys.stderr)
@@ -46,6 +80,9 @@ def get_connection(cls, decode_responses=True) -> Redis:
4680
except redis.exceptions.RedisError as e:
4781
print(f"Redis error: {e}", file=sys.stderr)
4882
raise
83+
except redis.exceptions.ClusterError as e:
84+
print(f"Redis Cluster error: {e}", file=sys.stderr)
85+
raise
4986
except Exception as e:
5087
print(f"Unexpected error: {e}", file=sys.stderr)
5188
raise

0 commit comments

Comments
 (0)