Files
turnstone/examples/mcp-cluster-ops
Patrick Buckley 2888e8ce0a feat: MCP cluster-ops example — reference MCP server + SDK implementa… (#55)
* feat: MCP cluster-ops example — reference MCP server + SDK implementation

Standalone MCP server under examples/mcp-cluster-ops/ that exposes
tools for executing commands across a Turnstone cluster via the MQ
client SDK. Serves as a reference implementation for both MCP server
patterns (FastMCP, lifespan, tool handlers) and TurnstoneClient usage.

4 tools: list_nodes, run_on_node, run_on_nodes, run_on_all_nodes.
Parallel dispatch via asyncio.gather, raw ToolResultEvent output
capture, UTF-8 safe truncation, input validation, concurrency caps.

35 tests, ruff clean, mypy --strict clean.

* fix: address review feedback on MCP cluster-ops example

- Remove REDIS_SSL support (RedisBroker doesn't accept ssl kwarg)
- Move max-nodes check from _dispatch_parallel into tool handlers
  for consistent error shape (always returns {"error": ...} object)
- Propagate KeyboardInterrupt/SystemExit from asyncio.gather instead
  of swallowing them as per-node failures
- Fix _truncate omitted bytes count to reflect actual bytes dropped
  after multi-byte boundary adjustment
- Apply strip/dedup to node IDs in run_on_all_nodes (matching
  run_on_nodes behavior)
- Add __name__ guard to __main__.py
- Fix misleading UTF-8 byte count comment in tests
2026-03-14 02:23:43 -07:00
..

MCP Cluster Ops

An MCP server that exposes tools for executing commands across a Turnstone cluster. Serves as a reference implementation for both MCP server patterns and Turnstone MQ client SDK usage.

How it works

This server uses Turnstone's MQ client (TurnstoneClient) to dispatch shell commands to specific nodes via Redis. Remote agents execute the command and the raw bash output is captured directly from the ToolResultEvent stream — bypassing the costly "agent reads output → re-generates output as completion tokens" round-trip.

Multi-node dispatches run in parallel via asyncio.gather, so total wall time is bounded by the slowest node rather than the sum.

Tools

Tool Description
list_nodes Discover active nodes in the cluster
run_on_node Execute a command on a specific node
run_on_nodes Execute a command on selected nodes in parallel
run_on_all_nodes Execute a command on ALL active nodes in parallel

Prerequisites

  • A running Turnstone cluster (at least one turnstone-server + turnstone-bridge)
  • Redis accessible from wherever this MCP server runs
  • Python 3.11+

Installation

# From the turnstone repo root:
pip install -e ./examples/mcp-cluster-ops

# Or install turnstone with MQ support first, then the example:
pip install -e ".[mq]"
pip install -e ./examples/mcp-cluster-ops

Configuration

Environment Variables

Variable Default Description
REDIS_HOST localhost Redis host
REDIS_PORT 6379 Redis port
REDIS_PASSWORD (none) Redis password (use env vars, not config files)
MCP_CLUSTER_OPS_TIMEOUT 120 Default command timeout (seconds, clamped 5-3600)
MCP_CLUSTER_OPS_MAX_OUTPUT 8192 Max output bytes per node (0 = unlimited)
MCP_CLUSTER_OPS_MAX_NODES 32 Max concurrent node dispatches
MCP_CLUSTER_OPS_MAX_COMMAND 65536 Max command string length

Register with Turnstone

TOML (~/.config/turnstone/config.toml):

[mcp.servers.cluster-ops]
command = "mcp-cluster-ops"

[mcp.servers.cluster-ops.env]
REDIS_HOST = "redis.example.com"

JSON (via --mcp-config):

{
  "mcpServers": {
    "cluster-ops": {
      "command": "mcp-cluster-ops",
      "env": {
        "REDIS_HOST": "redis.example.com"
      }
    }
  }
}

Usage Examples

Once registered, the tools appear in any Turnstone session. The model can:

> Check disk usage across the cluster

[calls list_nodes → discovers node-1, node-2, node-3]
[calls run_on_all_nodes with "df -h /"]

node-1: /dev/sda1  500G  320G  180G  64%  /
node-2: /dev/sda1  500G  410G   90G  82%  /
node-3: /dev/sda1  1.0T  200G  800G  20%  /

Why MQ client instead of HTTP SDK?

The HTTP SDK (TurnstoneServer) talks to a single server instance. The MQ client (TurnstoneClient) routes through Redis with target_node support, which is the entire point of cross-node cluster operations.

Security Considerations

This MCP server grants the calling agent shell access to cluster nodes.

  • Commands are executed with auto_approve=True and the privileges of the Turnstone server process on the target node.
  • Command output (which may contain secrets, credentials, or sensitive data) is returned through the MCP tool result and becomes part of the LLM context.
  • The security boundary is at the MCP host layer -- use Turnstone's tool policy system to restrict which agents can invoke these tools.
  • Set REDIS_PASSWORD via your environment or a secrets manager -- avoid hardcoding passwords in config files.

Development

cd examples/mcp-cluster-ops

# Run tests
pip install -e ".[test]"
pytest

# Lint
pip install -e ".[dev]"
ruff check mcp_cluster_ops/
mypy --strict mcp_cluster_ops/