Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Additional tools.

## Installation

The Redis MCP Server supports the `stdio` [transport](https://modelcontextprotocol.io/docs/concepts/transports#standard-input%2Foutput-stdio). Support to the `stremable-http` transport will be added in the future.
The Redis MCP Server supports both `stdio` and `streamable-http` [transports](https://modelcontextprotocol.io/docs/concepts/transports).

> No PyPi package is available at the moment.
Expand All @@ -89,10 +89,26 @@ uvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --url "re
# Run with individual parameters
uvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --host localhost --port 6379 --password mypassword

# Run with streamable HTTP transport (default on http://127.0.0.1:8000/mcp)
uvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --transport streamable-http --url redis://localhost:6379/0

# Run with streamable HTTP on custom host/port
uvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --transport streamable-http --http-host 0.0.0.0 --http-port 8080 --url redis://localhost:6379/0

# See all options
uvx --from git+https://github.com/redis/mcp-redis.git redis-mcp-server --help
```

### Running with Streamable HTTP

```sh
# Development mode with streamable HTTP
uv run redis-mcp-server --transport streamable-http --url redis://localhost:6379/0

# Production mode with custom host and port
uv run redis-mcp-server --transport streamable-http --http-host 0.0.0.0 --http-port 8000 --url redis://localhost:6379/0
```

### Development Installation

For development or if you prefer to clone the repository:
Expand All @@ -110,6 +126,12 @@ uv sync
# Run with CLI interface
uv run redis-mcp-server --help

# Run with stdio transport (default)
uv run src/main.py

# Run with streamable HTTP transport
uv run src/main.py --transport streamable-http --http-host 127.0.0.1 --http-port 8000

# Or run the main file directly (uses environment variables)
uv run src/main.py
```
Expand Down Expand Up @@ -365,7 +387,7 @@ The procedure will create the proper configuration in the `claude_desktop_config

### VS Code with GitHub Copilot

To use the Redis MCP Server with VS Code, you must nable the [agent mode](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode) tools. Add the following to your `settings.json`:
To use the Redis MCP Server with VS Code, you must enable the [agent mode](https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode) tools. Add the following to your `settings.json`:

```json
{
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ classifiers = [
dependencies = [
"mcp[cli]>=1.9.4",
"redis>=6.0.0",
"dotenv>=0.9.9",
"python-dotenv>=0.9.9",
"numpy>=2.2.4",
"click>=8.0.0",
"uvicorn>=0.23.0",
"starlette>=0.27.0",
]

[project.scripts]
Expand Down
1 change: 1 addition & 0 deletions src/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Redis MCP Server package."""
4 changes: 4 additions & 0 deletions src/common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright Redis Contributors
# SPDX-License-Identifier: MIT

"""Common utilities for Redis MCP Server."""
149 changes: 121 additions & 28 deletions src/common/config.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,55 @@
import sys

from dotenv import load_dotenv
import os
import urllib.parse

load_dotenv()

REDIS_CFG = {"host": os.getenv('REDIS_HOST', '127.0.0.1'),
"port": int(os.getenv('REDIS_PORT',6379)),
"username": os.getenv('REDIS_USERNAME', None),
"password": os.getenv('REDIS_PWD',''),
"ssl": os.getenv('REDIS_SSL', False) in ('true', '1', 't'),
"ssl_ca_path": os.getenv('REDIS_SSL_CA_PATH', None),
"ssl_keyfile": os.getenv('REDIS_SSL_KEYFILE', None),
"ssl_certfile": os.getenv('REDIS_SSL_CERTFILE', None),
"ssl_cert_reqs": os.getenv('REDIS_SSL_CERT_REQS', 'required'),
"ssl_ca_certs": os.getenv('REDIS_SSL_CA_CERTS', None),
"cluster_mode": os.getenv('REDIS_CLUSTER_MODE', False) in ('true', '1', 't'),
"db": int(os.getenv('REDIS_DB', 0))}

class RedisConfig:
"""Redis configuration management class."""

def __init__(self):
self._config = {
"host": os.getenv('REDIS_HOST', '127.0.0.1'),
"port": int(os.getenv('REDIS_PORT', 6379)),
"username": os.getenv('REDIS_USERNAME', None),
"password": os.getenv('REDIS_PWD', ''),
"ssl": os.getenv('REDIS_SSL', False) in ('true', '1', 't'),
"ssl_ca_path": os.getenv('REDIS_SSL_CA_PATH', None),
"ssl_keyfile": os.getenv('REDIS_SSL_KEYFILE', None),
"ssl_certfile": os.getenv('REDIS_SSL_CERTFILE', None),
"ssl_cert_reqs": os.getenv('REDIS_SSL_CERT_REQS', 'required'),
"ssl_ca_certs": os.getenv('REDIS_SSL_CA_CERTS', None),
"cluster_mode": os.getenv('REDIS_CLUSTER_MODE', False) in ('true', '1', 't'),
"db": int(os.getenv('REDIS_DB', 0))
}

@property
def config(self) -> dict:
"""Get the current configuration."""
return self._config.copy()

def get(self, key: str, default=None):
"""Get a configuration value."""
return self._config.get(key, default)

def __getitem__(self, key: str):
"""Get a configuration value using dictionary syntax."""
return self._config[key]

def update(self, config: dict):
"""Update configuration from dictionary."""
for key, value in config.items():
if key in ['port', 'db']:
# Keep port and db as integers
self._config[key] = int(value)
elif key in ['ssl', 'cluster_mode']:
# Keep ssl and cluster_mode as booleans
self._config[key] = bool(value)
else:
# Store other values as-is
self._config[key] = value if value is not None else None


def parse_redis_uri(uri: str) -> dict:
"""Parse a Redis URI and return connection parameters."""
Expand Down Expand Up @@ -83,17 +115,78 @@ def parse_redis_uri(uri: str) -> dict:
return config


def set_redis_config_from_cli(config: dict):
for key, value in config.items():
if key in ['port', 'db']:
# Keep port and db as integers
REDIS_CFG[key] = int(value)
elif key == 'ssl' or key == 'cluster_mode':
# Keep ssl and cluster_mode as booleans
REDIS_CFG[key] = bool(value)
elif isinstance(value, bool):
# Convert other booleans to strings for environment compatibility
REDIS_CFG[key] = 'true' if value else 'false'
else:
# Convert other values to strings
REDIS_CFG[key] = str(value) if value is not None else None
def build_redis_config(url=None, host=None, port=None, db=None, username=None,
password=None, ssl=None, ssl_ca_path=None, ssl_keyfile=None,
ssl_certfile=None, ssl_cert_reqs=None, ssl_ca_certs=None,
cluster_mode=None, host_id=None):
"""
Build Redis configuration from URL or individual parameters.
Handles cluster mode conflicts and parameter validation.

Returns:
dict: Redis configuration dictionary
str: Generated host_id if not provided
"""
# Parse configuration from URL or individual parameters
if url:
config = parse_redis_uri(url)
parsed_url = urllib.parse.urlparse(url)
# Generate host_id from URL if not provided
if host_id is None:
host_id = f"{parsed_url.hostname}:{parsed_url.port or 6379}"
else:
# Build config from individual parameters
config = {
"host": host or "127.0.0.1",
"port": port or 6379,
"db": db or 0,
"username": username,
"password": password or "",
"ssl": ssl or False,
"ssl_ca_path": ssl_ca_path,
"ssl_keyfile": ssl_keyfile,
"ssl_certfile": ssl_certfile,
"ssl_cert_reqs": ssl_cert_reqs or "required",
"ssl_ca_certs": ssl_ca_certs,
"cluster_mode": cluster_mode # Allow None for auto-detection
}
# Generate host_id from host:port if not provided
if host_id is None:
host_id = f"{config['host']}:{config['port']}"

# Override individual parameters if provided (useful when using URL + specific overrides)
# Only override URL values if the parameter was explicitly specified
if url is None or (host is not None and host != "127.0.0.1"):
if host is not None:
config["host"] = host
if url is None or (port is not None and port != 6379):
if port is not None:
config["port"] = port
if url is None or (db is not None and db != 0):
if db is not None:
config["db"] = db
if username is not None:
config["username"] = username
if password is not None:
config["password"] = password
if ssl is not None:
config["ssl"] = ssl
if ssl_ca_path is not None:
config["ssl_ca_path"] = ssl_ca_path
if ssl_keyfile is not None:
config["ssl_keyfile"] = ssl_keyfile
if ssl_certfile is not None:
config["ssl_certfile"] = ssl_certfile
if ssl_cert_reqs is not None:
config["ssl_cert_reqs"] = ssl_cert_reqs
if ssl_ca_certs is not None:
config["ssl_ca_certs"] = ssl_ca_certs
if cluster_mode is not None:
config["cluster_mode"] = cluster_mode

# Handle cluster mode conflicts
if config.get("cluster_mode", False):
# Remove db parameter in cluster mode as it's not supported
config.pop('db', None)

return config, host_id
Loading