feat(mcp): opt-in local Guide telemetry [roadmap:v0.10.4] - #72
Merged
Conversation
Implements rac/roadmaps/v0.10.x-guide/v0.10.4-guide-telemetry.md (contract). Adds: - v0.10.4 roadmap: opt-in local Guide telemetry milestone - ADR-039: opt-in, default-off, local-only, content-free telemetry; share flow is a user-submitted GitHub issue, no network code in RAC
Implements rac/roadmaps/v0.10.x-guide/v0.10.4-guide-telemetry.md. Adds: - rac.mcp.telemetry: stdlib-only recorder, pinned JSONL event schema, size rotation, corruption-tolerant read-back, summary, share URL - build_server recorder parameter; run_server telemetry flag with stderr notice; tool payloads return byte-identical (ADR-032 holds)
…10.4] Implements rac/roadmaps/v0.10.x-guide/v0.10.4-guide-telemetry.md. Adds: - rac mcp --telemetry: explicit opt-in, announced on stderr - rac mcp-stats [--json | --share]: local summary, JSON export, and a prefilled usage-report issue URL (the user's browser transmits) - guide-usage-report issue form for shared reports
…0.4] Implements rac/roadmaps/v0.10.x-guide/v0.10.4-guide-telemetry.md. Adds: - telemetry battery: default-off, pinned event schema, content-free guard, byte-identical payloads with and without a recorder, write-failure tolerance, rotation, corrupted-log read-back - mcp-stats goldens (human, JSON, share URL) over a fixture log Existing Guide goldens are untouched — the wire contract did not move.
Implements rac/roadmaps/v0.10.x-guide/v0.10.4-guide-telemetry.md. Adds: - docs/mcp.md telemetry section: what is recorded, what never is, the log path, read-back, and the share flow - docs/cli.md entries for mcp (--telemetry) and mcp-stats - README trust bullet and changelog entry
…0.4] ADR-039 was claimed by the Lore server identity decision merged on main while this branch was in flight; the telemetry decision and every reference to it move to ADR-040. No behavior change.
tcballard
force-pushed
the
claude/mcp-telemetry-opt-in-0nucfg
branch
from
June 12, 2026 17:20
3ecb1c3 to
7622dd9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements
rac/roadmaps/v0.10.x-guide/v0.10.4-guide-telemetry.md.Adds:
rac mcp --telemetryappends one JSONL event per tool call (counts and metadata only — never arguments or repository content) to$XDG_STATE_HOME/rac/guide-telemetry.jsonlrac mcp-stats [--json | --share]: local read-back summary;--jsonis the shareable export;--shareprints a prefilled GitHub usage-report issue URL the user reviews and submits in their own browsermcp-statsgoldens, aguide-usage-reportissue form, and documentation indocs/mcp.md,docs/cli.md, and the README trust sectionRoadmap / ADR Trace
Roadmap:
rac/roadmaps/v0.10.x-guide/v0.10.4-guide-telemetry.mdRelevant ADRs:
rac/decisions/adr-040-guide-local-telemetry.md(new — opt-in, local-only, content-free telemetry; numbered 040 because ADR-039 was claimed by the Lore server identity decision merged on main while this branch was in flight)rac/decisions/adr-032-guide-stateless-reads.md(tool output stays a pure function of repository bytes + input)rac/decisions/adr-035-byo-ai-credentials.md(no RAC cloud dependency)rac/decisions/adr-030-guide-tools-only-surface.md(tool descriptions untouched)rac/decisions/adr-034-guide-agent-reasoning-boundary.md(outcome classification reads the structured error tokens)Scope
Included
rac.mcp.telemetry(stdlib-only): pinned event schema, per-process random session id, single-generation 1 MB rotation, never-raise posture (a recorder that cannot write disables itself for the process)build_server(..., recorder=None)additive parameter; each tool body lifted to a module-level function and routed throughtelemetry.observe, which returns the payload unchangedrun_server(..., telemetry_enabled=False)with a one-line stderr notice when on — opt-in is announced, never silentrac mcp-statshuman and JSON output; exit0for any produced summary including an empty or missing log.github/ISSUE_TEMPLATE/guide-usage-report.ymland excludes the local log path (a home-directory path can embed a username); RAC contains no network codemcp-statsgoldens over a committed fixture log with a relativeXDG_STATE_HOMEso golden output stays machine-independentExcluded
Product / Architecture Decisions
observereturns the serialized payload byte-for-byte, and the log is never an input to a response — ADR-032's determinism survives intact and is pinned by tests_get_artifact,_search_artifacts,_get_related,_get_summary) rather than wrapped by a generic decorator, so FastMCP's signature-derived input schemas and the verbatim ADR-030 descriptions are untouchedrac.mcp.telemetry, notrac.services— services may not import the server layer, and the module's stdlib-only imports satisfy the isolation battery by constructiontruncatedfield is always present (explicit booleans make counting trivial), unlike the wire payload where absence-means-false is the pinned contract; the log is a separate contractmcp-statsis a new flat subcommand rather than an extension ofrac stats, whose exit-code semantics are tied to corpus content$XDG_STATE_HOME/rac/(state, not config or data), matching the Explorer workspace precedentUser-Facing Contract
CLI
Human Output
rac mcp --telemetryannounces on stderr:rac mcp: telemetry on — appending tool-call events (no arguments, no content) to ...followed by the log path (stdout stays protocol-only)rac mcp-statsprints the log path, event/session counts, first/last timestamps, a per-tool usage table (calls, errors, truncated, avg ms), and a skipped-unreadable-lines note when nonzero; with no log it prints guidance: telemetry is off by default, enable withrac mcp --telemetryJSON Output
{ "schema_version": "1", "path": "local log path (omitted from the --share payload)", "event_count": "total events", "session_count": "distinct per-process session ids", "first_ts": "ISO 8601 UTC or null", "last_ts": "ISO 8601 UTC or null", "skipped_lines": "unreadable lines tolerated during read-back", "tools": "per-tool rows: tool, calls, errors, truncated, avg_duration_ms" }Event lines in the log carry the pinned field set:
schema_version,ts,session,tool,outcome(ok|error|exception),error(structured token, error outcomes only),duration_ms,truncated.Exit Codes
0: mcp server shutdown on client disconnect; mcp-stats summary produced, including from an empty or missing log2: usage error (--jsonwith--share;mcp --rootnot a directory)Verification
Ran
Manual end-to-end with a real stdio client against the dogfood corpus: drove
get_summary,search_artifacts, and a not-foundget_artifactwith--telemetry— three events recorded with correct outcome classification (including a genuinely truncatedget_summaryand thenot-founderror token), no arguments or content in the log,rac mcp-statsand--jsonconsistent. Re-ran the same client without the flag: zero new lines.Covered
run_serverwithout the flag builds no recorder--json/--sharemutual exclusion exits2Review Path
rac/decisions/adr-040-guide-local-telemetry.mdandrac/roadmaps/v0.10.x-guide/v0.10.4-guide-telemetry.md— the contractsrc/rac/mcp/telemetry.py— recorder, schema, read-back, share URLsrc/rac/mcp/server.py— body lift andobserverouting (diff is mostly indentation-neutral moves)src/rac/cli.py,src/rac/output/human.py,src/rac/output/json.py— flag,mcp-stats, rendererstests/test_mcp_telemetry.py,tests/test_golden.py,tests/fixtures/telemetry/— the battery and goldensdocs/mcp.md,docs/cli.md,README.md,.github/ISSUE_TEMPLATE/guide-usage-report.yml— user-facing surfaceNotes For Reviewer
--sharepayload deliberately omits thepathfield from the--jsonshape: the report is counts and timestamps only, and a state-home path can embed a username. The share golden pins this.?template=...&report=...) is the one external behavior assumed; the roadmap records it as an assumption. If GitHub drops prefill,--sharedegrades to an unfilled form — the JSON export path is unaffected.tests/test_mcp_isolation.pyrequired no changes — the stdlib-only telemetry module satisfies the existing consumer-boundary rules by construction.Implementation Process
Implemented with AI assistance under the roadmap contract.
Final scope, review, and acceptance decisions were made by the maintainer.