Skip to content

(mcp): auto-install both qsvmcp and mcp binaries#3613

Merged
jqnatividad merged 4 commits into
masterfrom
mcp-install-both-qsvmcp-n-mcp
Mar 14, 2026
Merged

(mcp): auto-install both qsvmcp and mcp binaries#3613
jqnatividad merged 4 commits into
masterfrom
mcp-install-both-qsvmcp-n-mcp

Conversation

@jqnatividad

Copy link
Copy Markdown
Collaborator

No description provided.

jqnatividad and others added 3 commits March 14, 2026 14:26
The installer previously only extracted qsvmcp from the release archive.
Now it installs both qsvmcp (required) and qsv (optional) so users also
get the CLI binary for shell use, completions, and --update-mcp-skills.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rors

Strengthen psEscape to reject PowerShell metacharacters (backticks, $,
semicolons, pipes, etc.) as defense-in-depth against command injection
via crafted temp directory paths. Wrap execFileSync calls in
findBinaryInArchive with try/catch so a missing `find` binary on
minimal containers returns null instead of crashing the installer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add \r\n to the psEscape rejection regex to prevent PowerShell
single-quoted string breakout via newline injection. Also remove the
raw path from the error message to avoid echoing unsanitized input.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the MCP setup/install flow so the GitHub Releases installer extracts and installs both qsvmcp (MCP server) and qsv (CLI), and reports both paths back to the setup tool response.

Changes:

  • Extend installer to locate/install both qsvmcp and qsv from the downloaded release archive.
  • Change install result contract from a single binaryPath to binaryPaths (supporting both binaries).
  • Update qsv_setup success messaging to display both installed binaries when available.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
.claude/skills/src/mcp-tools.ts Displays installed binary path(s) after successful setup/revalidation.
.claude/skills/src/installer.ts Installs both qsvmcp and qsv, adds archive search helper, updates manual instructions.

Comment thread .claude/skills/src/mcp-tools.ts Outdated
Comment thread .claude/skills/src/installer.ts
Comment thread .claude/skills/src/installer.ts
Adjust the post-install message to report either the full qsv toolkit or the qsv MCP server depending on whether paths?.qsv is present. This makes the installation output accurate when only the MCP server is available, improving user guidance for subsequent data tasks.
@jqnatividad jqnatividad merged commit 2c8ed1f into master Mar 14, 2026
26 checks passed
@jqnatividad jqnatividad deleted the mcp-install-both-qsvmcp-n-mcp branch March 14, 2026 19:46
jqnatividad added a commit that referenced this pull request Jul 11, 2026
…ow (roborev #3613)

The nested `- "gauge_range"` / `- "target"` bullets inside the --dictionary
Options help began with a dash, so the docopt-based help generator parsed them
as an option definition and emitted a spurious `-, -` row in docs/help/viz.md.
Reword both hints as flowing prose (no leading dashes) and regenerate the help;
the --dictionary row is now a single clean entry that still documents both hints.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jqnatividad added a commit that referenced this pull request Jul 12, 2026
…e measures (#4185)

* feat(describegpt): emit x-qsv.gauge_range KPI hint for canonical-scale measures

Teaches the `--dictionary --infer-content-type` pass to propose a `gauge_range`
[min, max] for numeric MEASURE columns on a canonical scale (percent, ratio,
rating, bounded index), which `viz smart --dictionary` reads as
`x-qsv.gauge_range` to draw a gauge KPI tile instead of a bare number.

Follows the existing propose-then-verify discipline (same as null_values):
- LlmDictField carries the raw model proposal; parse_llm_dictionary_response
  only shape-validates ([min,max] of two finite numbers, min<max), gated on
  --infer-content-type.
- combine_dictionary_entries (and the two-pass baseline variant) keep it ONLY
  when the finalized role is `measure` AND the observed [min,max] fits inside
  the proposed scale — a range the data spills past is dropped (verify_gauge_range,
  run after role coercion). An unverified gauge is simply not emitted, so no-flag
  and non-measure runs stay byte-identical.
- build_x_qsv emits it as the 2-element array viz consumes.

`target` (delta baseline) is deliberately NOT emitted: a business goal is
unfalsifiable from the data, so an LLM would fabricate one. Deferred; viz's
delta path stays dormant until a hand-authored dictionary supplies a target.

The refine prompt is intentionally left untouched: like role/concept, the
first-pass gauge_range survives the refine overlay via baseline preservation
(locked by two_pass_refine_omitting_gauge_range_preserves_the_baseline), so the
DEFAULT_DICTIONARY_REFINE_PROMPT guard test stays green.

Caveats: the guardrail catches scale OVERFLOW, not wrong-scale (a 0-1 ratio the
model mistags [0,100] still passes containment); and whether a given LLM returns
sensible ranges is not CI-testable — verify with a live QSV_VIZ_REGEN_LLM=1 run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(describegpt): scope gauge_range to renderable continuous measures (roborev #3606)

Medium: viz's build_kpi_row only turns a column into a gauge KPI tile when it
earned a box/violin/histogram distribution panel, which low-cardinality integer
ratings never do — so the prompt's 1-to-5 / 0-to-10 rating examples advertised
gauges viz cannot render. Narrowed the prompt to CONTINUOUS canonical scales
(percentage, probability/ratio, bounded continuous index) and added an explicit
clause telling the model NOT to gauge discrete/low-cardinality ratings. (Gauging
panel-less measures in viz is left as a follow-up enhancement.)

Low: verify_gauge_range now also requires the column's qsv type to be
Integer/Float, so a String column the LLM mis-tags role:measure whose
lexicographic min/max happen to parse as floats can no longer keep/emit a gauge.

New test combine_drops_gauge_range_for_non_numeric_type; prompt-gating assertion
updated to the new bullet wording.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(viz): render KPI tile labels as wrapped subtitles below each indicator

Long auto-generated KPI labels (e.g. "Mean Profit Margin Percentage") rendered
as the plotly Indicator's built-in title — a large font above the number — and
overran narrow tiles, colliding with neighbours. Move the label out of the
indicator title into a smaller, word-wrapped subtitle annotation centered BELOW
each tile: kpi_indicator now owns only the number/gauge/delta and reserves the
bottom band of its domain, and both render paths (typed grid + inline) draw the
label via kpi_label_annotation. wrap_kpi_label greedily wraps on word boundaries
(never hard-splitting a word); tile width (tile count) sets the wrap threshold.

Verified in-browser on the gemma-inferred sales_sample gauge dashboard: labels
sit cleanly below each tile, and the longest wraps to two lines with no overlap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viz): enlarge KPI subtitle labels (13px) below each indicator

The previous KPI subtitle labels were too small (10px) to read comfortably.
Keep them below the indicator (not as titles) but bump the font to 13px, widen
the reserved bottom band to 30% to fit two wrapped lines, and tighten the wrap
threshold so long labels still break cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viz): tighten the gap between KPI numbers and their labels

The label sat at the very bottom of the reserved band, a full band below the
indicator's number (which has its own bottom padding), reading as too detached.
Anchor the label up inside that padding (14% of the band above the reserved
line) in both render paths so it sits snug under the number.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viz): keep raised KPI label off the gauge; per-tile label position (roborev #3610)

The previous commit raised every KPI label uniformly into the indicator domain,
which is safe for number-only tiles (empty padding below the centered number)
but risks overlapping gauge tiles — the always-present Completeness gauge and
any dictionary-backed measure gauge sit lower in the domain. Compute label_y
per tile in both render paths: number-only tiles keep the raised position (snug
under the number), gauge tiles anchor at the reserved line, fully below the
indicator domain so the label can never land on the dial.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viz): align all KPI labels on one baseline under the numbers

The per-tile label split (roborev #3610) dropped gauge-tile labels to a lower
line, creating an uneven, distracting baseline across the KPI row. Visual review
shows the gauge dial is a semicircle in the TOP of the domain with its number
below it, so the label band directly under the number is clear of the dial —
the overlap #3610 guarded against does not occur at the label's actual position.
Anchor every label (number-only AND gauge) at the same raised y for one clean
baseline snug under the numbers, with no dial overlap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(viz,describegpt): document the KPI overview row & gauge_range/target hints

The KPI overview row (Completeness gauge + headline measure tiles), and the
x-qsv.gauge_range / x-qsv.target dictionary hints that turn a measure tile into
a gauge or a "vs target" delta, were undocumented. Add them to the viz `smart`
Overview-panels list and the --dictionary help, and note gauge_range emission
under describegpt --infer-content-type (clarifying target is hand-authored, not
inferred). Regenerated docs/help/{viz,describegpt}.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(viz): reword KPI hint bullets as prose to fix bogus help-table row (roborev #3613)

The nested `- "gauge_range"` / `- "target"` bullets inside the --dictionary
Options help began with a dash, so the docopt-based help generator parsed them
as an option definition and emitted a spurious `-, -` row in docs/help/viz.md.
Reword both hints as flowing prose (no leading dashes) and regenerate the help;
the --dictionary row is now a single clean entry that still documents both hints.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(viz): move Completeness to the header; add KPI gauge/delta gallery example

Completeness was a permanent KPI gauge tile that read ~100% for most datasets
and crowded the overview row. Move it to a quiet "Completeness:" line in the
dashboard header table (between Columns: and Compiled:), and drop it from the
KPI row - which now leads purely with the headline MEASURES and returns None
when a dataset has no measure to headline (e.g. a map-only dataset, so its map
reclaims panel-0). build_kpi_row no longer needs the row count.

Add a deterministic gallery example (examples/viz/sales_kpi_dict.schema.json +
gen_gallery.py entry -> smart_sales_kpi.html) that showcases both dictionary KPI
hints on sales_sample: x-qsv.gauge_range [0,1] draws Discount % / Profit Margin %
as GAUGES, and x-qsv.target 0.25 on Profit Margin adds a "vs target" DELTA
(mean 0.21 -> red -0.042). Hand-authored, so it regenerates without a live LLM.
Regenerated the gallery (39 figures) and the affected smart dashboards (their
completeness now shows in the header).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viz): force-add the gallery KPI dictionary fixture (gitignored *.json)

sales_kpi_dict.schema.json is referenced by gen_gallery.py and drives the
committed smart_sales_kpi.html, but .gitignore's `*.json` rule dropped it from
the previous commit. Force-add it (matching the sibling *_dict.schema.json
fixtures) so the gallery example is reproducible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(viz): sync KPI-row help text with header-completeness change (roborev #3615)

The viz smart help still described the KPI row as leading every dashboard with a
built-in Completeness gauge, but completeness now lives in the header metadata
table and the KPI row is omitted when a dataset has no headline measure. Reword
the "KPI overview row" bullet accordingly and regenerate docs/help/viz.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants