Skip to content

feat: Add MCP (Model Context Protocol) server for AI integration #475

@joshrotenberg

Description

@joshrotenberg

Summary

Add an MCP (Model Context Protocol) server mode to redisctl, enabling AI systems to manage Redis Cloud and Enterprise through a standardized tool interface.

What is MCP?

Model Context Protocol is an open standard for AI-tool integration introduced by Anthropic and adopted by OpenAI. It allows AI systems to discover and invoke external tools through a JSON-RPC interface.

┌─────────────────┐         JSON-RPC          ┌─────────────────┐
│   AI System     │ ◄──────────────────────►  │   MCP Server    │
│ (Claude, etc.)  │   - list_tools            │   (redisctl)    │
│                 │   - call_tool             │                 │
└─────────────────┘                           └─────────────────┘

Why MCP vs Shell/CLI?

MCP provides advantages over shelling out to redisctl directly:

Concern Shell Out MCP
Error handling Parse stderr strings Structured error types
Parameters String interpolation JSON schema validation
Discovery Read --help Programmatic tool listing
Async ops Poll CLI repeatedly Native async tracking
Output Parse text/JSON Typed responses

Why This Matters

AI agent frameworks that need Redis Cloud/Enterprise access currently have to build and maintain their own API clients. redisctl-mcp eliminates this by exposing our existing, tested handlers as MCP tools.

What consumers get:

  • 100% API coverage (50 handlers already implemented)
  • Proper async operation handling (our --wait logic)
  • Context-rich error messages
  • Credential isolation (keys never exposed to AI systems)
  • Zero maintenance burden (we maintain it, they consume it)

Non-Goals

  • Not a CLI replacement - The standard CLI remains the primary interface for humans
  • Not embedding LLM logic - This exposes tools; reasoning happens in the AI system
  • Not doing RAG/knowledge base - No Redis documentation or runbook retrieval
  • Not auto-executing commands - AI decides, redisctl executes what it is asked

Prototype Spec

New Crate

crates/
└── redisctl-mcp/
    ├── Cargo.toml
    └── src/
        ├── lib.rs              # Server setup, handler registration
        ├── cloud_tools.rs      # Cloud API tools
        ├── enterprise_tools.rs # Enterprise API tools
        └── config.rs           # Credential/profile loading

CLI Command

# Start MCP server (stdio transport, read-only by default)
redisctl mcp serve

# Explicit read-only mode (signals intent)
redisctl mcp serve --read-only

# With specific profile
redisctl mcp serve --profile production

# List available tools (debugging)
redisctl mcp tools

Phase 1 Tools (Read-Only)

Based on common AI agent needs for Redis management:

Redis Cloud (8 tools)

Tool Description Handler
cloud_account_get Account information account.rs
cloud_subscriptions_list List all subscriptions subscriptions.rs
cloud_subscription_get Subscription details subscriptions.rs
cloud_databases_list Databases in subscription databases.rs
cloud_database_get Database configuration databases.rs
cloud_tasks_list Recent async tasks tasks.rs
cloud_task_get Task status tasks.rs
cloud_logs_list Account activity logs logs.rs

Redis Enterprise (8 tools)

Tool Description Handler
enterprise_cluster_get Cluster information cluster.rs
enterprise_nodes_list List cluster nodes nodes.rs
enterprise_databases_list List all databases bdbs.rs
enterprise_database_get Database configuration bdbs.rs
enterprise_database_stats Database statistics stats.rs
enterprise_shards_list Shard placement shards.rs
enterprise_alerts_list Active alerts alerts.rs
enterprise_logs_get Cluster event logs logs.rs

Example Implementation

use rmcp::{ServerHandler, tool};
use redis_cloud::CloudClient;

pub struct RedisCloudTools {
    client: CloudClient,
}

#[tool(
    name = "cloud_databases_list",
    description = "List all databases in a Redis Cloud subscription",
    params(subscription_id = "The subscription ID")
)]
async fn list_databases(&self, subscription_id: u64) -> Result<Value> {
    let dbs = self.client.databases().list(subscription_id).await?;
    Ok(serde_json::to_value(dbs)?)
}

#[tool(
    name = "cloud_database_get",
    description = "Get configuration for a specific Redis Cloud database",
    params(
        subscription_id = "The subscription ID",
        database_id = "The database ID"
    )
)]
async fn get_database(&self, subscription_id: u64, database_id: u64) -> Result<Value> {
    let db = self.client.databases().get(subscription_id, database_id).await?;
    Ok(serde_json::to_value(db)?)
}

Observability

All tool invocations should be logged for audit purposes:

[2024-01-15T10:23:45Z INFO  redisctl_mcp] Tool called: cloud_database_get
[2024-01-15T10:23:45Z DEBUG redisctl_mcp]   params: {"subscription_id": 12345, "database_id": 67890}
[2024-01-15T10:23:46Z INFO  redisctl_mcp]   result: success (235ms)

This provides an audit trail when AI systems are making infrastructure queries.

Integration Examples

Claude Desktop (~/.config/claude/claude_desktop_config.json):

{
  "mcpServers": {
    "redisctl": {
      "command": "redisctl",
      "args": ["mcp", "serve", "--profile", "production"]
    }
  }
}

Docker Sidecar:

services:
  my-agent:
    image: my-ai-agent:latest
    depends_on:
      - redisctl-mcp
      
  redisctl-mcp:
    image: redis/redisctl:latest
    command: ["mcp", "serve", "--transport", "sse", "--port", "3000"]
    environment:
      - REDIS_CLOUD_API_KEY=${REDIS_CLOUD_API_KEY}
      - REDIS_CLOUD_SECRET_KEY=${REDIS_CLOUD_SECRET_KEY}

Python MCP Client:

from mcp import ClientSession, StdioServerParameters

server = StdioServerParameters(
    command="redisctl",
    args=["mcp", "serve"],
    env={"REDIS_CLOUD_API_KEY": "...", "REDIS_CLOUD_SECRET_KEY": "..."}
)

async with ClientSession(*server) as session:
    # List tools
    tools = await session.list_tools()
    
    # Call a tool
    result = await session.call_tool(
        "cloud_database_get", 
        {"subscription_id": 12345, "database_id": 67890}
    )

Transport Options

stdio (Phase 1 default)

  • Server runs as subprocess, communication via stdin/stdout
  • How Claude Desktop and Claude Code work
  • Zero network configuration

SSE/HTTP (Phase 4)

  • Server runs as HTTP service on configurable port
  • Enables remote access (MCP server on bastion host)
  • Shared access (team members connecting to same server)
  • Required for non-stdio clients

Dependencies

  • rmcp - Official Rust MCP SDK
  • Existing redis-cloud and redis-enterprise crates

Future Phases

Phase Scope
Phase 1 16 read-only tools, stdio transport
Phase 2 Write operations with --write flag, safety confirmations
Phase 3 MCP Resources for context (subscription summaries, cluster overview)
Phase 4 SSE/HTTP transport for network access
Phase 5 MCP Prompts for guided workflows

References

Acceptance Criteria

  • redisctl-mcp crate with 16 read-only tools
  • redisctl mcp serve command (stdio transport)
  • redisctl mcp serve --read-only flag (default, explicit intent)
  • redisctl mcp tools command to list available tools
  • Tool invocation logging for audit trail
  • Works with Claude Desktop
  • Documentation with setup instructions
  • Integration tests

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions