Merge pull request #1166 from modelcontextprotocol/force-bump-redis-and-memory

Add missing capabilities and improve connection handling for server-redis
This commit is contained in:
Ola Hungerford
2025-04-03 06:46:11 -07:00
committed by GitHub
2 changed files with 87 additions and 12 deletions

View File

@@ -2,6 +2,31 @@
A Model Context Protocol server that provides access to Redis databases. This server enables LLMs to interact with Redis key-value stores through a set of standardized tools. A Model Context Protocol server that provides access to Redis databases. This server enables LLMs to interact with Redis key-value stores through a set of standardized tools.
## Prerequisites
1. Redis server must be installed and running
- [Download Redis](https://redis.io/download)
- For Windows users: Use [Windows Subsystem for Linux (WSL)](https://redis.io/docs/getting-started/installation/install-redis-on-windows/) or [Memurai](https://www.memurai.com/) (Redis-compatible Windows server)
- Default port: 6379
## Common Issues & Solutions
### Connection Errors
**ECONNREFUSED**
- **Cause**: Redis server is not running or unreachable
- **Solution**:
- Verify Redis is running: `redis-cli ping` should return "PONG"
- Check Redis service status: `systemctl status redis` (Linux) or `brew services list` (macOS)
- Ensure correct port (default 6379) is not blocked by firewall
- Verify Redis URL format: `redis://hostname:port`
### Server Behavior
- The server implements exponential backoff with a maximum of 5 retries
- Initial retry delay: 1 second, maximum delay: 30 seconds
- Server will exit after max retries to prevent infinite reconnection loops
## Components ## Components
### Tools ### Tools

View File

@@ -7,10 +7,26 @@ import {
import { z } from "zod"; import { z } from "zod";
import { createClient } from 'redis'; import { createClient } from 'redis';
// Get Redis URL from command line args or use default // Configuration
const REDIS_URL = process.argv[2] || "redis://localhost:6379"; const REDIS_URL = process.argv[2] || "redis://localhost:6379";
const MAX_RETRIES = 5;
const MIN_RETRY_DELAY = 1000; // 1 second
const MAX_RETRY_DELAY = 30000; // 30 seconds
// Create Redis client with retry strategy
const redisClient = createClient({ const redisClient = createClient({
url: REDIS_URL url: REDIS_URL,
socket: {
reconnectStrategy: (retries) => {
if (retries >= MAX_RETRIES) {
console.error(`Maximum retries (${MAX_RETRIES}) reached. Giving up.`);
return new Error('Max retries reached');
}
const delay = Math.min(Math.pow(2, retries) * MIN_RETRY_DELAY, MAX_RETRY_DELAY);
console.error(`Reconnection attempt ${retries + 1}/${MAX_RETRIES} in ${delay}ms`);
return delay;
}
}
}); });
// Define Zod schemas for validation // Define Zod schemas for validation
@@ -36,7 +52,12 @@ const ListArgumentsSchema = z.object({
const server = new Server( const server = new Server(
{ {
name: "redis", name: "redis",
version: "1.0.0" version: "0.0.1"
},
{
capabilities: {
tools: {}
}
} }
); );
@@ -215,22 +236,51 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
// Start the server // Start the server
async function main() { async function main() {
try { try {
// Connect to Redis // Set up Redis event handlers
redisClient.on('error', (err: Error) => console.error('Redis Client Error', err)); redisClient.on('error', (err: Error) => {
await redisClient.connect(); console.error('Redis Client Error:', err);
console.error(`Connected to Redis successfully at ${REDIS_URL}`); });
redisClient.on('connect', () => {
console.error(`Connected to Redis at ${REDIS_URL}`);
});
redisClient.on('reconnecting', () => {
console.error('Attempting to reconnect to Redis...');
});
redisClient.on('end', () => {
console.error('Redis connection closed');
});
// Connect to Redis
await redisClient.connect();
// Set up MCP server
const transport = new StdioServerTransport(); const transport = new StdioServerTransport();
await server.connect(transport); await server.connect(transport);
console.error("Redis MCP Server running on stdio"); console.error("Redis MCP Server running on stdio");
} catch (error) { } catch (error) {
console.error("Error during startup:", error); console.error("Error during startup:", error);
await redisClient.quit(); await cleanup();
process.exit(1);
} }
} }
// Cleanup function
async function cleanup() {
try {
await redisClient.quit();
} catch (error) {
console.error("Error during cleanup:", error);
}
process.exit(1);
}
// Handle process termination
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
main().catch((error) => { main().catch((error) => {
console.error("Fatal error in main():", error); console.error("Fatal error in main():", error);
redisClient.quit().finally(() => process.exit(1)); cleanup();
}); });