Add Grok Bot remote MCP harness (streamable HTTP pilot) - #20
Conversation
Expose the existing MCP tool surface over authenticated streamable HTTP (+ legacy SSE) so Grok Bot can supervise durable jobs without a fake grok-bot worker adapter. Default supervise scope omits implement/edit; stdio MCP stays unchanged. Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
Follow-up validation (no feature expansion)Regression / containment proof on 1) Diff surface (non-doc code only)
Untouched (confirmed via
Remote imports stdio handlers ( 2) Full test suite (CI command)python -m unittest discover -s tests -v
# Ran 1784 tests in 119.636s
# FAILED (failures=1, skipped=2)
3) Stdio MCP still works (Cursor path)Subprocess
Installer handshake ( Contrast remote supervise: 32 tools, 4) Adapters inventory
5) CLI non-regression
PASS/FAIL matrix
*Local full-suite asterisk: one agentic test fails identically on |
Deterministic HTTP loop (initialize/list/doctor/start/status/show/401/scope) on an ephemeral port with no tunnel; lock stdio tool floor and adapter inventory (no grok-bot); document AddMcpServer steps and optional PoC script. Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
Pushed: e2e + regression guards (
|
…session CORS. Strict remote clients were getting initialize 200s then tools=0 because we hardcoded protocolVersion 2024-11-05, returned bare JSON when Accept listed SSE, and did not expose Mcp-Session-Id to browsers. Negotiate versions, prefer SSE frames, CORS-expose the session header, and lock the Cursor/Grok handshake in GrokBotHandshakeRegressionTests. Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
Hotfix pushed: Grok Bot handshake (
|
Extract build_remote_initialize_result so protocolVersion echo and
non-empty capabilities.tools cannot regress to 2024-11-05 / tools:{}.
Strengthen GrokBotHandshakeRegressionTests to assert the result body;
audit initialize with the negotiated protocol for live debugging.
Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
Initialize body harden pushed
Result body contract
Tests
Operator noteIf live smoke still shows # restart
python -m puppetmaster mcp serve-remote --scope implement --allow-origin '*'
# curl check — must print 2025-03-26
curl -sS -X POST "$URL/mcp" \
-H "Authorization: Bearer $PUPPETMASTER_MCP_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}' \
| python -c 'import sys,json; print(json.load(sys.stdin)["result"]["protocolVersion"], json.load(open("/dev/stdin")) if False else "")'Simpler check: look for audit line |
POST /mcp returns application/json when Accept lists application/json (or is empty/*/*); SSE only when Accept is text/event-stream without JSON. Fixes Failed to load MCP server when clients send both media types but parse initialize as JSON. Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
Dual-Accept → JSON fix (
|
Streamable HTTP clients open GET /mcp after initialize; closing after one comment looked like a dead server and triggered Grok Bot re-init loops. Keep the stream open with periodic SSE comments, audit open/close, and document supervise scope for smaller Plugins tool lists. Dual-Accept still prefers JSON. Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
GET
|
Echo any non-empty protocolVersion, drop experimental/instructions, and compact remote tools/list schemas so Plugins stays connected (tools=50). Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
Fail closed for the supervise surface, reject unsafe Origins and oversized or sessionless requests, and document the reviewed remote tool boundary.
Summary
puppetmaster/mcp_remote.py) wrapping the existing tool handlers over Streamable HTTP (/mcp) plus legacy SSE (/sse+/message), so Grok Bot can act as a pilot against Puppetmaster’s durable job/artifact runtime. Stdio MCP is unchanged.--scope superviseexposes doctor / start review|plan|swarm / status|logs|live artifacts|show / CodeGraph reads; implement/edit/browser/mutating tools stay off until--scope implement. Bearer auth is mandatory; Origin guard, per-IP rate limit, and JSONL audit ship with the server.python -m puppetmaster mcp serve-remote(+puppetmaster-mcp-remote). Docs:docs/GROK_BOT.md. Optional PoC:./scripts/grok-bot-remote-poc.sh/make grok-bot-poc. Version 1.22.0.connected/ tools=50) tipb00adfa:protocolVersion(default only if missing — no allowlist).{"tools":{"listChanged":false},"logging":{}}— noexperimental, noinstructions.build_remote_initialize_resultnever calls stdiohandle_message.application/json; SSE only whentext/event-streamwithout JSON./mcplong-lived SSE keepalives (no instantConnection: close).remote_tool_to_jsonfor remotetools/list: desc ≤280, prop desc ≤120, simple schema fields,additionalProperties: false, required ⊆ properties.Mcp-Session-Id.This is not a
grok-botworker adapter. Stdio tool surface and worker adapters are untouched.Test plan
tests/test_mcp_remote.py— auth, scope, dual-Accept → JSON, SSE-only → SSE, protocol echo (incl. unknown version), minimal caps,remote_tool_to_jsoncompaction /additionalProperties: falsetests/test_mcp_remote_e2e.py— full HTTP loop +GrokBotHandshakeRegressionTests(initialize echo + dual Accept JSON + GET keepalive + compact tools/list)tests/test_harness_regression_guards.py— stdio tool floor + adapters inventory (no grok-bot)mcp_server.pyhandler editsconnected, tools=50 (was Failed to load / tools=0)Follow-ups
grok-botworker adapter only if/when Grok Bot publishes dispatch