Future MCP + HTTP wrappers (deferred to v2)¶
miner's v1 transport surface is the CLI. The MCP and HTTP server wrappers — originally scoped for Phase 6 — have been deferred to v2.
Rationale: miner's primary consumer (the RadiusRed Quant agent) drives miner via subprocess.Popen + JSONL on stdout without any shape gap. Running a 24x7 server for a single-operator tool adds operational surface (deploy, monitor, restart, secret rotation, body-size caps, rate limits, mTLS) that is not justified for v1. The placeholder crates crates/miner-mcp/ and crates/miner-http/ stay in the workspace so a v2 contributor knows exactly where the implementations land; each is currently twelve lines emitting one tracing::info! message pointing at this doc.
This doc captures the planned design at the architectural-sketch level only — what each wrapper would expose, why the shapes were chosen, planned crate dependencies with risk notes, and the pointers a v2 contributor needs to pick up the work. The implementation details (route table with HTTP status codes, request / response JSON examples, MCP tool-schema fragments, cancellation propagation diagrams, content-negotiation rules, rmcp-vs-fallback decision tree) belong in the v2 plan-phase's RESEARCH.md and CONTEXT.md, not here.
The two reclassified requirements PLAT-v2-07 (MCP) and PLAT-v2-08 (HTTP) — see .planning/REQUIREMENTS.md and .planning/STATE.md Deferred Items — are the tracking handles for the v2 implementation work. Both inherit the contracts locked by v1: byte-identical-JSONL parity against miner scan / miner sweep, sync-core + async-edges discipline (FOUND-04), and the locked Finding envelope vocabulary documented in findings_envelope.md.
What MCP would expose¶
The MCP wrapper would mirror miner's CLI surface as typed MCP tools:
- One MCP tool per registered scan —
stats.autocorr.ljung_box@1,stats.stationarity.adf@1,cross.cointegration.engle_granger@1, ... Each tool's parameter schema derives directly from the scan'sScan::params_schema()JSON Schema — no duplicated source of truth. The tool result is the streamed JSONL ofFindingenvelopes the underlyingengine::run_onecall produces, framed as MCP streaming tool-result chunks. - Meta-tools for discovery and preflight:
list_scans— mirrorsminer scans; emits the catalogue as a single MCP tool result (one entry per registered scan withscan_id,arity,param_schema,finding_fields).list_symbols— enumerates the(symbol, side)pairs available in the configuredMINER_CACHE_ROOTby walking the reader directory layout.probe— preflight a parameter set without executing; equivalent tominer scan <id> --dry-runbut reachable from MCP without invoking the streaming engine.- Transports: stdio (for local-agent attachment in the LSP-style spawning model) and streamable-HTTP / SSE (for remote agents).
- Open design question for the v2 plan-phase: one-tool-per-scan vs a single generic
scantool that takesscan_id@versionas a parameter. The former gives strong typing per tool and lets MCP clients render scan-specific parameter UIs; the latter shrinks the tool count from ~23 to ~5 (one per CLI subcommand) and avoids registry / MCP tool-list synchronisation. v1 has no opinion; the v2 plan-phase decides.
Deep design: .planning/research/ARCHITECTURE.md §8 (Wrapper Crates).
What HTTP would expose¶
The HTTP wrapper would expose miner's CLI shape over REST + streaming:
GET /v1/scans— catalogue introspection; returns the same JSONL stream asminer scansbut as a JSON array body (or NDJSON when the client signals streaming viaAccept: application/x-ndjson).GET /v1/symbols— instrument-side inventory; same shape as the MCPlist_symbolstool.POST /v1/scan— single scan; request body is the resolvedScanRequestJSON; response body streamsFindingenvelopes as NDJSON or SSE per content negotiation.POST /v1/sweep— sweep manifest; request body is either the TOML manifest (preferred;Content-Type: application/toml) or the parsedSweepManifestJSON; response streams findings and the closingSweepSummaryenvelope.- Content negotiation: NDJSON by default; SSE (
text/event-stream) when the client prefers a framed event stream. Open design question for v2 — both are reasonable; NDJSON matches the CLI byte-for-byte and is preferred for v2. - Out of v1 sketch scope: authentication strategy (bearer / mTLS / OAuth), body-size cap, rate limits, bind address, TLS termination. The v2 plan-phase owns these — they are deployment-posture decisions, not engine-shape decisions.
- Response framing: every streaming response endpoint emits one Finding envelope per NDJSON line or one Finding envelope per SSE
data:field. The byte payload is byte-identical to the correspondingminer scanstdout for the same inputs — the HTTP wrapper is a framing, not a transformation. - Cancellation: HTTP client disconnect translates to engine cancellation via the same
Cancelledflag the CLI'sctrlchandler sets. The v2 plan-phase decides the exact propagation path (theaxumconnection-tracker mechanism ortower::timeout::Timeoutmiddleware are both plausible carriers).
Deep design: same source.
Crate choices¶
Sourced from .planning/research/STACK.md.
- MCP —
rmcp. The Rust SDK published bymodelcontextprotocol/rust-sdkis the canonical pick. STACK.md confidence is MEDIUM and the row is explicitly marked VERIFY: the SDK version, transport support (stdio + streamable-HTTP), streaming tool-result chunks, and tokio-version compatibility all need re-confirming when the v2 plan-phase begins. The risk is that the SDK is the youngest dependency in miner's stack and may have shifted shape between v1 docs-freeze and v2 implementation kickoff. - Fallback if
rmcpdoes not fit: a hand-rolled JSON-RPC-over-stdio binary built directly againstserde_json. STACK.md estimates ~500 LOC. The MCP wire protocol is well-defined and stable; hand-rolling it loses streaming-tool-result ergonomics but otherwise produces a working MCP server. This fallback does NOT affect the HTTP wrapper — the two wrappers are independent crates. - HTTP —
axumovertokio+tower. Confidence HIGH in STACK.md.axum0.7+ ontokio1.40+ withtower-httpmiddleware (Tracefor request logging,Compressionfor gzip / br,Timeoutfor upstream cancellation propagation). Streaming response bodies viaaxum::body::Body::from_streamover atokio_stream::StreamofResult<Bytes, Error>chunks bridges cleanly into NDJSON or SSE framing. - Async bridge —
tokio::task::spawn_blocking. This is the canonical pattern and a non-negotiable invariant:miner-coreMUST stay sync + rayon. The wrappers offload everyengine::run_one/sweep::run_sweepinvocation into the blocking pool viatokio::task::spawn_blocking, channel findings out as the engine emits them, and feed the channel into the response stream. FOUND-04 (cargo tree -p miner-core --edges normal,buildshows zero async deps) is the CI gate that pins this — it must remain green after v2 lands. - Logging —
tracing+tracing-subscriber. Already in the workspace; the wrappers inherit miner's structured-log discipline (stderr-only, structured spans for request / scan / job). No new logging-stack choice to make. - Serialisation —
serde+serde_json. Reuse miner-core'sFindingtypes verbatim; the wrappers re-serialise viaserde_json::to_writerto the response body. No JSON-encoder swap (e.g.simd-json) until profiling shows encoding is hot.
Why deferred¶
Three short reasons:
- The CLI is enough for v1. miner's primary consumer (the Quant agent) drives miner via
subprocess.Popen+ line-by-line JSONL parsing. There is no shape gap between what the CLI emits and what the MCP / HTTP wrappers would emit — the wrappers would only re-frame the same byte stream. - Servers add operational surface a single-operator tool doesn't need. Deployment, monitoring, restart semantics, secret rotation, TLS, body-size caps, rate limits, abuse posture — none of these problems exist for a CLI that runs on the same host as its caller. Postponing them until there is a genuine remote-consumer requirement is the cheaper position.
rmcpis the highest-risk single dependency in the stack. Better to let the v2 plan-phase re-research the SDK against its then-current release than lock the choice now and discover a surprise (transport shape change, streaming-result API redesign, MSRV bump) on v2 day one.- No transport drift in v1. Because the wrappers never shipped in v1, no consumer has been written against a wrapper API that might shift in v2. The locked v1 surface is the CLI alone; the wrappers are a clean v2 greenfield with no migration debt to repay.
How to pick this up (v2 contributor)¶
The v2 plan-phase should read these in order before reopening the wrapper implementations:
.planning/phases/06-mcp-http-wrappers/06-CONTEXT.md— the discuss-phase output that captured the deferral rationale, the original wrapper-scope notes, and the locked surface decisions (D6-01 through D6-09). Start here..planning/research/ARCHITECTURE.md§8 (Wrapper Crates) — the layered design for the wrappers, including thetokio::task::spawn_blockingbridge sketch and the per-wrapper crate boundaries..planning/research/STACK.md— thermcprow (with the VERIFY risk note), theaxumrow, and thetower/tower-httprows. Re-validate the version pins against the then-current releases.crates/miner-mcp/src/main.rsandcrates/miner-http/src/main.rs— the placeholder anchor points. Each is twelve lines today emitting onetracing::info!line referencing this doc; v2 replaces the body and adds the wrapper-crate dependencies to the per-crateCargo.toml../architecture.md(the public-audience system map) — re-read the "Sync core + async edges" section. The async-edges discipline is the contract the v2 implementation must preserve.- The Phase 6 sign-off SUMMARY at
.planning/phases/06-mcp-http-wrappers/06-03-SUMMARY.md— captures the v1 docs-only invariants the v2 implementation must NOT break (zero new dependencies inminer-core/miner-cli; thecargo tree -p miner-core --edges normal,buildgate stays green; the lockedFindingenvelope stays additive-only).
CI invariants for the v2 wrapper implementation:
cargo tree -p miner-core --edges normal,buildMUST continue to return empty fortokio/axum/hyper/rmcp/tower. New deps land inminer-mcpandminer-http, NOT inminer-core.cargo clippy --workspace --all-targets -- -D warnings(CI gate 2) MUST stay green with the new wrapper code.- Wrapper integration tests must include a byte-identical-JSONL parity check against
miner scanstdout for at least one representative scan per family.
The byte-identical-JSONL parity expectation: every Finding emitted by an MCP tool/call response or an HTTP POST /v1/scan response body MUST be byte-identical to the corresponding miner scan stdout for the same inputs. The CLI is the wire-format anchor; the wrappers are framings, not transformations. The v2 plan-phase should ship parity tests (one per wrapper) that diff wrapper-output JSONL against miner scan stdout for a representative scan from each family (ANOM / CROSS / SEAS).
Out of scope for this sketch¶
Explicitly NOT part of this design sketch (per D6-03; tracked for the v2 plan-phase). The bullets below are pointers, not contracts — the v2 RESEARCH.md and CONTEXT.md will turn each into a concrete decision:
- Route table with HTTP status codes (which paths return
404vs400vs422, error-body shape, retry-after semantics). - Request / response JSON examples (the v1 docs/findings_envelope.md already covers the response shape; only the wrapper-specific request shape is missing).
- MCP tool-schema fragments (the typed
inputSchema/outputSchemaper tool; mechanically generated from the registry but worth a v2 sample). - Cancellation propagation diagrams (how MCP
notifications/cancelledand HTTP client-disconnect translate into engineCancelledflag setting). - Content-negotiation decision tree (NDJSON vs SSE vs JSON-array; default-vs-honour-Accept policy).
rmcpvs hand-rolled JSON-RPC decision tree (the v2 plan-phase re-runsgsd-researchonrmcpagainst the then-current release and decides).- Deployment posture artefacts (Dockerfile, helm chart, systemd unit). Out of v2 sketch scope; a follow-up operational-readiness phase owns these.
Tracked for v2 milestone planning. The v1 docs/ folder is the binding context for the v2 wrapper implementation; nothing in this sketch is normative beyond the contracts already locked in v1 (envelope shape, byte-identical-JSONL parity, FOUND-04 async-edges discipline).
License¶
Licensed under the Apache License, Version 2.0. See: https://www.apache.org/licenses/LICENSE-2.0
Copyright 2026 Radius Red Ltd. | Contact