Files
turnstone/examples/mcp-cluster-ops
Patrick Buckley 664d44c109 fix(examples): rewrite mcp-cluster-ops to use console SDK for cluster… (#278)
* fix(examples): rewrite mcp-cluster-ops to use console SDK for cluster routing

The example MCP server was broken after the direct HTTP transport
refactor — it used TurnstoneServer (single-node) for cluster ops that
require TurnstoneConsole (cluster gateway). Rewrites dispatch flow to:
route via console → SSE stream from node → cleanup via console.

- Switch from TurnstoneServer to TurnstoneConsole for node listing and
  workstream routing (TURNSTONE_CONSOLE_URL replaces TURNSTONE_SERVER_URL)
- Add proper workstream lifecycle: create via routing proxy, stream from
  node, close in finally block with leak-safe ws_id guard
- Catch dispatch exceptions in run_on_node for structured JSON errors
- Extract _extract_node_ids helper, remove dead n.get("id") fallback
- Normalise _console_kwargs to always include token key
- Rewrite tests against Console+Server mocks (36 → 44 tests)

* fix(examples): paginate node listing and clarify auth in README

Address Copilot review feedback on #278:
- _list_nodes_sync now paginates via offset/limit loop so clusters
  with >100 nodes are fully discovered
- README step 2 now mentions token passthrough for authenticated clusters
- New test_paginates_large_clusters verifies multi-page fetch (45 tests)
2026-04-02 15:12:41 -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 SDK usage.

How it works

This server uses the Turnstone console SDK (TurnstoneConsole) for node discovery and routing, and TurnstoneServer for per-node SSE streaming. The dispatch flow for each command is:

  1. RouteTurnstoneConsole.route_create_workstream(target_node=..., auto_approve=True) creates a workstream pinned to the target node via the console's hash-ring routing proxy, returning ws_id and node_url.
  2. ExecuteTurnstoneServer(node_url, token=...) connects directly to the node's SSE stream using the same TURNSTONE_API_TOKEN. send_and_wait(prompt, ws_id) runs the command and the raw bash output is captured from the ToolResultEvent — bypassing the costly "agent reads output then re-generates output as completion tokens" round-trip.
  3. CleanupTurnstoneConsole.route_close(ws_id) closes the workstream.

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 with at least one turnstone-server and a turnstone-console
  • Python 3.11+

Installation

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

Configuration

Environment Variables

Variable Default Description
TURNSTONE_CONSOLE_URL http://localhost:8090 Console URL for node discovery and routing
TURNSTONE_API_TOKEN (none) API token / JWT for authentication
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]
TURNSTONE_CONSOLE_URL = "http://console.example.com:8090"

JSON (via --mcp-config):

{
  "mcpServers": {
    "cluster-ops": {
      "command": "mcp-cluster-ops",
      "env": {
        "TURNSTONE_CONSOLE_URL": "http://console.example.com:8090"
      }
    }
  }
}

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%  /

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 TURNSTONE_API_TOKEN via your environment or a secrets manager -- avoid hardcoding tokens 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/