Installing ray-mcp into an AI agent¶
This guide walks through building ray-mcp and connecting it to an MCP-capable
AI agent (Claude Desktop, Claude Code, Cursor, or any MCP client). It also shows
how to verify the connection by hand.
Status: v0.1.0 preview. The server exposes 19 tools across three permission tiers — read (always on), write (
--allow-mutations), and destructive (--allow-destructive) — covering RayClusters, RayJobs, and RayServices. It runs over both the stdio transport (this guide) and a streamable HTTP transport with bearer-token / TokenReview auth (see HTTP transport & auth). By default it is read-only. The full, always-in-sync tool list is the Tool reference.
1. Install the binary¶
Fastest — install straight from the module with Go (no clone):
go install github.com/risjai/ray-mcp/cmd/ray-mcp@latest # or @v0.1.0 to pin
This drops ray-mcp in $(go env GOBIN) (or $(go env GOPATH)/bin). Get its
absolute path for the agent config below:
echo "$(go env GOBIN)/ray-mcp" # if GOBIN is set
echo "$(go env GOPATH)/bin/ray-mcp" # otherwise
Or build from a clone:
git clone https://github.com/risjai/ray-mcp.git
cd ray-mcp
go build -o ray-mcp ./cmd/ray-mcp # produces ./ray-mcp
echo "$(pwd)/ray-mcp" # absolute path for the agent config
Requires Go 1.26.3+ (see go.mod).
2. Flags you'll likely set¶
| Flag | Default | What it does |
|---|---|---|
--default-namespace <ns> |
in-cluster ns, else default |
namespace used when a tool omits one |
--allow-all-namespaces |
off | serve every namespace instead of just --default-namespace; see note below |
--context <name> |
current kubeconfig context | which kubeconfig context to bind |
--kubeconfig <path> |
$KUBECONFIG / discovery |
credentials source |
--allow-mutations |
off | register write tools (create/update/scale/submit/deploy) |
--allow-destructive |
off | register destructive tools (delete) — also needs --allow-mutations |
--allow-raw-spec |
on | expose the rawSpec power-user escape hatch on write tools; set --allow-raw-spec=false to prune it from the schemas |
--log-level <level> |
info |
debug/info/warn/error (logs go to stderr) |
Read tools are always on. By default the server is read-only — you must opt into mutations explicitly. Full flag reference: Configuration (and design spec §9). To serve over HTTP instead of stdio, see HTTP transport & auth.
--allow-all-namespacesis reconciled against your RBAC at boot. On startup the server runs aSelfSubjectAccessReviewfor cluster-wideliston the Ray CRDs (rayclusters,rayjobs,rayservices.ray.io). If the ServiceAccount is not authorized cluster-wide, the server refuses to start (grant aClusterRolecovering all three, or drop the flag) rather than lying about its scope. If the cluster can't be reached to run the check, it downgrades to namespaced scope (a warning to stderr) so an offline boot still works.ray_capabilitiesreports the reconciled truth:allNamespacesandservedNamespaces. With the flag off, passingallNamespaces: trueto a list tool is refused at the edge.The server always boots, even with no reachable cluster — so your agent always connects and
ray_capabilitiesalways works. The cluster connection is made lazily, on the firstray_cluster_*call; if the kubeconfig can't reach a cluster, those tools return a clean error (and retry once it's fixed — no restart needed). Point--context/--kubeconfigat a context wherekubectl --context <name> get rayclustersworks.
3. Connect your agent¶
MCP clients launch the server as a subprocess and speak JSON-RPC over its stdin/stdout. You give the client a command + args.
Claude Desktop¶
Edit claude_desktop_config.json (macOS:
~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"ray-mcp": {
"command": "/absolute/path/to/ray-mcp",
"args": ["--default-namespace", "default"]
}
}
}
Restart Claude Desktop. ray-mcp should appear in the tools (🔌) menu, exposing
ray_capabilities.
Claude Code¶
claude mcp add ray-mcp /absolute/path/to/ray-mcp --default-namespace default
Then in a session, ask the agent to "call ray_capabilities" — or run
/mcp to see it listed.
Cursor / other MCP clients¶
Use the same shape — a stdio server with command = the binary path and args
= your flags. Consult your client's MCP docs for the config file location.
4. Verify by hand (no agent needed)¶
You can drive the server directly to confirm it works before wiring an agent. Pipe JSON-RPC frames into it over stdio:
{ printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"cli","version":"0"}}}';
sleep 0.4;
printf '%s\n' '{"jsonrpc":"2.0","method":"notifications/initialized"}';
printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ray_capabilities","arguments":{}}}';
sleep 0.4;
} | ./ray-mcp --default-namespace demo --allow-mutations 2>/dev/null
The last line is the tool result — structured + a human summary:
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"ray-mcp 0.0.0-dev | context (current-context) | default-namespace demo | scope: namespaced demo | tiers: read,write | KubeRay tested v1.6.1"}],"structuredContent":{"allNamespaces":false,"defaultNamespace":"demo","enabledTiers":["read","write"],"kubeContext":"(current-context)","kubeRayTested":"v1.6.1","servedNamespaces":["demo"],"serverVersion":"0.0.0-dev"}}}
Note enabledTiers became ["read","write"] because we passed
--allow-mutations; without it you'd see ["read"].
Troubleshooting¶
- Agent shows no tools / "server failed to start" — run the binary yourself
with the same flags; errors print to stderr. A bad
--http-addrbind or an invalid flag value makes the server refuse to boot with an explanatory message. - Garbled output / protocol errors — under stdio, stdout is the JSON-RPC wire. Don't redirect anything else to the server's stdout. All logs already go to stderr by design.
- Can't reach the cluster — that affects the cluster/job/service tools, not
ray_capabilities, which is config-only and makes no live call. Verifykubectl --context <name> get rayclustersworks.
What's next¶
- Browse the Tool reference — every tool's parameters, output, and an example call.
- Serve over HTTP with auth instead of stdio.
- Understand the safety tiers before enabling writes.
To contribute, see Contributing.