mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
664d44c109
* 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)
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:
- Route —
TurnstoneConsole.route_create_workstream(target_node=..., auto_approve=True)creates a workstream pinned to the target node via the console's hash-ring routing proxy, returningws_idandnode_url. - Execute —
TurnstoneServer(node_url, token=...)connects directly to the node's SSE stream using the sameTURNSTONE_API_TOKEN.send_and_wait(prompt, ws_id)runs the command and the raw bash output is captured from theToolResultEvent— bypassing the costly "agent reads output then re-generates output as completion tokens" round-trip. - Cleanup —
TurnstoneConsole.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-serverand aturnstone-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=Trueand 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_TOKENvia 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/