Skip to main content

VarianceLabs MCP

MCP server exposing VarianceLabs mineral-estimation & reporting tools for agentic workflows: QAQC of assays and drillhole data, a 3D viewer, property modelling (block-model estimation: NEXUS non-stationary geostatistics by default, LVA-DKNN neural), geometry (implicit wireframes, block models) and EDA (exploratory data analysis of samples and estimates).

  • src/variancelabs_mcp/ — MCP server and tool layers: qaqc/, project/, processing/, modelling/, geometry/, eda/, app/ (the localhost app server and its committed bundle)
  • src/project/ — projects: objects (drillholes, QC data, composites, wireframes, block models, estimates) with ids, recipes and links between them, rebuilds, the files the viewer and Data page read; no MCP dependency
  • src/data_processing/ — drillhole desurvey + compositing engine; the single source of hole geometry (the viewer uses it too); no MCP dependency
  • src/deep_modelling/ — property-modelling engine, ported from the deep-modelling research repo; no MCP dependency, so the web app can call it too
  • src/nexus/ — NEXUS estimation engine (torch, CUDA when present; no scipy); runs inside the estimate worker
  • src/geometry/ — spatial geometry engine: wireframes (RBF + marching cubes) and block models (rotated, sub-blocked, wireframe-constrained); no MCP dependency
  • src/eda/ — EDA engine: statistics and plot data from arrays (univariate, bivariate, outliers, section, grade–tonnage, swath); src/project/eda.py feeds it project objects
  • src/qaqc/ — QAQC engine: CRM control charts (Nelson rules), blanks, duplicates, grade, drillhole-geometry and spatial checks, flags and settings, from arrays and tables; src/project/qaqc.py feeds it project objects; no MCP dependency
  • src/web-app/ — browser app (Data, QAQC and EDA pages, 3D viewer)
  • doc/varianceLabs-doc/ — user guide for geologists (one Markdown page per module, figures in figures/), built into the PDF committed at src/variancelabs_mcp/doc/, which doc_open opens

Develop

uv sync --extra dev --extra modelling      # modelling = PyTorch (CUDA build on Win/Linux)
uv run python -m variancelabs_mcp.server   # stdio MCP server
uv run pytest                              # default test suite (~3 min)
uv run pytest -m integration               # slow end-to-end: real worker, demo data
uv run python scripts/benchmark_nexus.py   # NEXUS on the synthetic truths of scripts/generate_synthetic_3d.py
uv run python scripts/build_doc.py         # user guide PDF (Chrome or Edge; VL_CHROME = path)

After editing doc/varianceLabs-doc/, rebuild the guide and commit the PDF with its .json: tests/test_doc.py fails when the PDF is out of date or a tool is missing from the guide.

Install as an MCP server (one-time)

Installed editable as a uv tool, then registered at user scope so the tools are available in every project — not just this repo.

uv tool install --python 3.11 --editable ".[modelling]"  # repo root; drop [modelling] to skip torch
claude mcp add --scope user variancelabs ~/.local/bin/variancelabs-mcp

On Windows the executable is %USERPROFILE%\.local\bin\variancelabs-mcp.exe; register that absolute path. uv tool run variancelabs-mcp does not work — the package is not on any index, so the registration must point at the exe.

Verify with claude mcp list (expect variancelabs: … - ✓ Connected).

Updating an installed MCP

Because the install is editable, its .pth points straight at this repo's src/. The server runs whatever is in your working tree — including after a git checkout of another branch.

You changed What to do
Python source (src/variancelabs_mcp/) Restart the Claude Code session, or /mcp → variancelabs → reconnect
Deps or [project.scripts] in pyproject.toml uv tool install --python 3.11 --editable ".[modelling]" --force, then restart
Web app (src/web-app/) npm run build (writes the committed bundle at src/variancelabs_mcp/app/static/), then restart

The tool venv is separate from .venv, which is why dependency changes need the --force reinstall — a plain restart will not pick them up. On Windows, stop every running server first (close the Claude sessions using it): --force deletes the venv's Lib/ and then fails on the locked Scripts/, leaving it broken. To repair or update in place while a server runs: uv pip install --python "%APPDATA%\uv\tools\variancelabs-mcp\Scripts\python.exe" -e ".[modelling]". The version recorded in the tool's metadata is baked at install time and can lag pyproject.toml; that is cosmetic, the code served is always the working tree.

QAQC tools (qaqc_*)

QC files (CRM results with certified values, duplicates, blanks) are loaded as a QC data object nested under raw drillholes; the checks of grades, drillhole geometry and spatial outliers need only the drillholes. Every check emits flags that can be reviewed.

  • project_load_qaqc(drillholes, standards?, reference?, duplicates?, blanks?, mapping?, name?, project?) — copy the QC files into the project; columns are detected and reported.
  • qaqc_overview(drillholes?) — start here: panels with data, verdicts, open flags, settings in force, suggested next tools.
  • qaqc_standards(element?, standard?, ...) — CRM bias and Nelson-rule events by batch.
  • qaqc_blanks(element?, ...) — contamination against the detection-limit threshold.
  • qaqc_duplicates(element?, set?, ...) — HARD precision and bias per pair set (duplicates, repeats, re-assay columns).
  • qaqc_checks(family, element?, ...) — grades, geometry or spatial checks.
  • qaqc_issues(family?, check?, severity?, status?, element?, hole?, limit?) — the flag register.
  • qaqc_review(status, flag_ids? | check?, element?, hole?, comment?, only_open?) — mark flags open, resolved or dismissed; reviews and settings are stored in <project>/qaqc.json.
  • qaqc_settings(values?) — detection limits, blank factor, HARD thresholds, minimum recovery, spike and spatial ratios ("auto" restores one).

Like EDA, each tool returns the numbers and a url opening the QAQC page (/qaqc/, shared chart core and UI in src/web-app/src/analysis/) on the same view; the page computes on demand through GET /api/qaqc/<panel>. Design: docs/superpowers/specs/2026-10-06-qaqc-module-design.md.

Data processing tools (processing_*)

  • processing_composite(drillholes, options?, name?, project?) — fixed-length composites with X/Y/Z at midpoints, added to the project (ready for modelling_start_run). drillholes is a drillholes/composites object id or {collar, intervals, survey?, mapping?, desurvey?} CSV paths.
  • processing_desurvey(collar, table, survey?, at?, options?) — X/Y/Z for intervals (midpoint or both ends) or depth points; a standalone export, not a project object.

Every option is optional: unset ones (composite length, value columns, coordinate system, dip sign) are chosen from the data, and the returned summary says what was used and why; desurvey method (minimum curvature by default, balanced tangent, tangent) and azimuth_correction (degrees) are fixed defaults. processing_composite writes into the project folder (objects/<object_id>/v<n>/); processing_desurvey writes to ./variancelabs-output/processing/<run-id>/ (*.csv + report.json). Design: docs/superpowers/specs/2026-09-27-data-processing-design.md.

Property modelling tools (modelling_*)

A run estimates one grade at every block of a block-model object, optionally domain by domain (hard boundaries); the result is an estimate object nested under its block model. Two methods (method, fixed per estimate):

  • nexus (default) — non-stationary geostatistics (NEXUS.md): stationarity audit, multiscale robust trend, local scale / nugget / anisotropy fields, kriging with a Paciorek–Schervish covariance, cross-validated settings; ordinary kriging when the data are stationary. Needs a hole id and at least 5 holes per domain.
  • lva_dknn — the neural estimator of src/deep_modelling/: local kriging with a learned covariance, conditioned on local data.

Runs execute in a detached worker (python -m project.estimate run <run_dir>), so the MCP server never loads torch; the worker commits the result into the project. The run folder runs/<object_id>/ keeps status.json and worker.log.

  • modelling_start_run(block_model, samples, value, method?, domains?, columns?, options?, name?, project?)
  • modelling_run_status(object_id, project?) — progress; metrics and output paths when done.
  • modelling_cancel_run(object_id, project?).

NEXUS outputs (objects/<object_id>/v<n>/): estimate.csv (<value>_est block mean, _trend, _p10/_p50/_p90, _sd, _rel_unc, _p_gt_<cutoff>, n_samples, n_holes, mean_dist, flag), metrics.json (audit, every parameter with its reason, held-out skill against ordinary kriging and inverse distance, calibration, trend reproduction), validation.csv, fields.npz. The Data page creates (+ Add estimate on a block model) and edits NEXUS estimates. An upstream rebuild marks the estimate out of date (project_update(object_id, {}) re-runs it). Designs: docs/superpowers/specs/2026-10-10-nexus-estimator-design.md, docs/superpowers/specs/2026-09-27-property-modelling-design.md, docs/superpowers/specs/2026-10-01-block-model-estimates-design.md.

Geometry tools (geometry_*)

Closed wireframes from drillholes: a biharmonic RBF (greedy-fitted, dual form) of the signed distance to inside/outside contacts, contoured by marching cubes. Every build is added to the project as an object.

  • geometry_domain_wireframe(drillholes, column, inside, options?, name?, project?) — solid around intervals whose code is in inside (e.g. LITH, ["SAP"]).
  • geometry_grade_shell(drillholes, column, cutoff, options?, name?, project?) — shell around composites ≥ cutoff (indicator by default; transform: log|none|auto interpolates the grade).

drillholes is a drillholes/composites object id or {collar, intervals, survey?, mapping?, desurvey?} CSV paths. Every option is optional (resolution, extent, trend — auto from the contacts —, kernel, drift, accuracy, max_centres, max_distance…); the summary says what was used and why. Outputs land in the project folder (objects/<object_id>/v<n>/): wireframe.obj in world coordinates, points.csv, report.json. Design: docs/superpowers/specs/2026-09-28-geometry-wireframes-design.md.

  • geometry_block_model(wireframes?, drillholes?, options?, name?, project?) — block-model geometry, no values yet: parent blocks (optionally rotated), regular sub-blocks where wireframe boundaries cut them, a DOMAIN code per block (first listed wireframe wins). Outputs block_model.csv (world centroids, sizes, IJK, sub-block indices, domain), block_model.json (definition) and report.json in the project folder; estimates attach to it (see Property modelling). Design: docs/superpowers/specs/2026-09-28-geometry-block-model-design.md.

EDA tools (eda_*)

Read-only analyses of the project's samples (drillholes / composites) and estimates. Each returns the numbers (statistics, tables, the choices made and why) and a url opening the EDA page on the same analysis (open=true opens it; or project_open(open="eda")).

  • eda_overview(project?) — what can be analysed, per-column summaries, suggested order.
  • eda_univariate(source?, property?, group_by?, weight?, log?, bins?) — statistics per group, histogram (length-weighted by default).
  • eda_bivariate(source?, x?, y?, group_by?, log?, method?) — Pearson / log / Spearman, regression, correlation matrix.
  • eda_outliers(source?, property?, group_by?, weight?) — top-cut diagnostics: decile analysis, cap candidates with metal removed, suggested cap, flagged samples.
  • eda_section(source?, start?, end?, width?, property?, wireframes?, block_model?) — vertical section along A–A′ with a corridor (auto: the collars' long axis); the wireframes (default all) and an estimate's or block model's blocks cut by its plane.
  • eda_grade_tonnage(estimate, density?, cutoffs?, domain?) — grade–tonnage curve (volume without a density).
  • eda_swath(estimate, axis?, width?, domain?) — estimate against its samples along X, Y, Z.

The page (/eda/) computes the same analyses on demand through GET /api/eda/catalog and GET /api/eda/<analysis> and draws them with the shared chart core of src/web-app/src/analysis/ (Canvas; zoom, pan, hover; no chart dependency), also used by the QAQC page. Design: docs/superpowers/specs/2026-10-01-eda-module-design.md.

Extending

  • New vertical: add a subpackage with register(mcp) and call it in server.build_server().

Projects (project_* tools)

Every loaded or built object (drillholes, composites, wireframes, block models, estimates) lives in a project under variancelabs-output/projects/<project-id>/. With a single project in the working folder, tools use it by default (creating and naming one after the folder the first time); with several, the tools ask which one. processing_* and geometry_* tools write their outputs into the project and return object_id. Each app page saves what it shows (camera, visibility, display settings, panel choices) in app-state/<page>.json inside the project, through GET/POST /api/state; saving is never a project change (no revision bump). Delete that folder to reset the pages.

  • project_open(project?, open?) — resolve (or default-create) a project; open: 'data' | 'qaqc' | 'eda' | 'viewer' opens that page in the browser. Returns the object list, summaries and the page URLs.
  • project_create(name, open?) — a new, empty project; same open as project_open.
  • project_load_drillholes(collar?, intervals?, survey?, desurveyed?, load_as?, mapping?, desurvey?, name?, project?) — load raw tables (editable later) or an already desurveyed/composited table (load_as: 'drillholes' | 'composites', not editable).
  • project_load_qaqc(drillholes, standards?, ...) — QC files for raw drillholes (see QAQC tools).
  • project_load_wireframe(path, name?, project?) — import an OBJ wireframe.
  • project_update(object_id, params, project?) — change an object's parameters (as on the Data page's Edit panel) and rebuild it and everything built from it.
  • project_remove(object_ids, cascade?, project?) — remove objects; dependents must be confirmed with cascade=true.
  • project_set_main_commodity(column, project?) — the column (e.g. AU1) every page and eda/qaqc tool shows first where an object has it; null clears it. No rebuild.
  • doc_open(open=true) — opens the user guide PDF in the default PDF viewer; returns its path, version and chapters.

Opening a page starts a localhost app server (one per MCP process, one token, ?token= in the URL) serving the built web app. Positions in the 3D viewer are staged relative to a local origin (float64 on the Python side) so UTM-scale coordinates render without float32 jitter; the grid labels show real-world coordinates. Numeric interval columns become elements; text columns with ≤ 64 distinct values (e.g. LITH, DOMAIN) are staged as categories.

Display colours holes by element: Continuous (linear or log scale, editable range, Auto = 2nd–98th percentile), Classed (grade shells by equal interval, quantile or custom breaks such as 0.3, 1, 3) or Categorical (text columns, or numeric codes with ≤ 64 values). Palettes are grouped as grade/sequential, topography, diverging and categorical; each can be reversed, and legends show metres per class or category.

Wireframes appear under their own tree node and are selectable in 3D like holes (holes win when both are under the cursor). The Display tab styles the selected wireframes: Solid · Solid + edges · Edges · Points, colour, edge colour and opacity; the Object tab gives volume / parts / extent cuts.

Block models are coloured by domain, block size or one of their estimates (palette and range; blocks without a value grey); the Object tab lists each estimate's held-out R². They are selectable in 3D like wireframes. Display › Rendering › Ray-traced draws the whole model from GPU 3D textures instead of its visible shell: Voxels or Translucent, opacity (blocks are otherwise opaque: unsorted instances cannot blend in depth order), and a cutoff (Show ≥ / ≤) on the estimate shown. It streams a multi-resolution chunk tree (sub-blocks → parents → coarser), finer where blocks cover more than a pixel, within a 1.5 GB GPU budget (src/web-app/src/viewer/volume/, staged by src/project/volume.py; /files/ answers HTTP range requests). A model with more than 2 M visible blocks is ray-traced only.

Viewer controls (the Project tree on the left; Properties pinned bottom-left with Display styling and Object facts about the selection):

Gesture Effect
Drag / right-drag / wheel orbit / pan / zoom
Double-click a hole, wireframe or block model (3D or tree) select only that one; on empty space, clear
Ctrl+click a hole or wireframe (3D or tree) add / remove it
Click, Shift+click, Ctrl+Shift+click (tree) select one / a range / add a range
↑ ↓, Shift+↑ ↓ (tree) move / extend the selection
Double-click Drillholes select all its holes
Ctrl+A select all of the selected type; again for everything
F frame the selection (everything if none)
Esc clear the selection

Web app (src/web-app/)

The browser side is a multi-page app: a left rail with the VarianceLabs mark (assets/icons/) above the module tiles (src/shell/), one page per module at /<module>/, each opened by its URL (?project=&token=, plus the view keys of an MCP link) so an MCP tool can open it directly; a page loads and saves what it shows with src/shell/pageState.ts. Four modules: data/ (the Data page, src/data/), qaqc/ (the QAQC page, src/qaqc/), eda/ (the EDA page, src/eda/) and viewer/ (the 3D viewer, src/viewer/); / redirects to viewer/. To add a module: a src/<module>/main.ts that calls mountShell("<module>"), a <module>/index.html, an entry in src/shell/modules.ts and one in the Vite input map.

  • uv run python scripts/dev_app_server.py creates (or reuses) a demo project with a synthetic dataset (add --large for the 10k-hole fluidity dataset, or --qaqc for data/test-dataset-QAQC with its QC files) and prints the page URLs.
  • npm run dev (in src/web-app/) hot-reloads the app, proxying /api and /files to that server (default http://127.0.0.1:8765, override with VL_PROXY).
  • npm run test (vitest) and npm run build (type-gate + bundle).
  • The bundle is committed at src/variancelabs_mcp/app/static/ so the MCP needs no Node at runtime: rebuild + commit after engine changes.

Metadata

Release files for variancelabs-mcp 0.3.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for variancelabs-mcp 0.3.4
File Size Uploaded Provenance
variancelabs_mcp-0.3.4.tar.gz 4.2 MB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for variancelabs-mcp 0.3.4
File Interpreter ABI Platform
variancelabs_mcp-0.3.4-py3-none-any.whl Python 3 none any Details

Total release size: 5.2 MB

Release files / variancelabs_mcp-0.3.4.tar.gz

Download URL variancelabs_mcp-0.3.4.tar.gz
Size 4.2 MB
Tags Source
SHA-256 checksum
How to use checksums
233408d451b11e9a187167197a40faa53fbfe508fc69d458a4344f627757ddc0
BLAKE2b-256 checksum
How to use checksums
6186ae2bee2ca63e8010e6aec82789714b245f667e78694e3e3047503a6fbaa3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / variancelabs_mcp-0.3.4-py3-none-any.whl

Download URL variancelabs_mcp-0.3.4-py3-none-any.whl
Size 942.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9d02bd7c30a4cd5aa539b39f8bfd47ccc651c33104c17e398972d3178973fc09
BLAKE2b-256 checksum
How to use checksums
0b437d3a151b80d0a13328e7d6d1f8f34fe103566949306d3f0b9894ec9a05cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for variancelabs-mcp 0.3.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page