Skip to main content

Read-only 3D model inspection tools for AI agents

Project description

MeshProbe

MeshProbe gives AI agents a small, typed interface for inspecting 3D assemblies without editing the source model. Agents can identify components, move a camera, switch between orthographic and perspective projection, control lens and lighting, hide occluders, mark parts, and request evidence renders or contact sheets.

MeshProbe starts a persistent, factory-clean Blender worker. It imports GLB, glTF, OBJ, or STL files and returns a validated scene manifest without changing the source file. It can render the current inspection state with Eevee or CUDA-backed Cycles, produce private component and highlight masks plus a Depth/Normal EXR for evaluators, rank line-of-sight occluders, and build a focused 3×3 contact sheet with perspective context and six orthographic detail views.

The Playwright CLI model, for 3D

If you have used Playwright CLI, MeshProbe's interaction model should feel familiar. Playwright opens a named browser session, applies small operations such as navigation and clicks, and keeps a compact page snapshot that an agent can query. MeshProbe does the same for a 3D model: it opens a named inspection session, applies camera, lighting, display, and marking operations, and writes compact state under .meshprobe.

That makes MeshProbe useful when an agent needs to investigate a model over several turns. The model is imported once. Later commands reuse the same Blender worker, and a daemon restart reconstructs the session from its last acknowledged checkpoint. The agent usually reads components.yml and state.yml with rg or yq; the complete manifest remains in scene.json for jq queries.

uv run meshprobe --session gearbox open models/gearbox.glb
uv run meshprobe --session gearbox find '**/idler*'
yq '.components[] | select(.name | test("idler"; "i"))' \
  .meshprobe/sessions/gearbox/components.yml
uv run meshprobe --session gearbox display c7 --mode isolated
uv run meshprobe --session gearbox illumination-set raking_left
uv run meshprobe --session gearbox render-sheet c7
uv run meshprobe --session gearbox view-frame c7
uv run meshprobe --session gearbox render-image --output idler.png

view-frame computes the camera that tightly fits a component's bounds and persists it like view-orbit, so a following render-image fills the frame with an isolated part instead of inheriting a camera framed for the whole assembly. The --focus option on the view-* commands is a projection diagnostic (it reports where components land in the image); it never moves the camera.

Normal output is a short receipt containing the result and state paths. Add --json or --yaml for a machine-readable receipt, or --raw when the full operation result is actually needed. meshprobe schema --kind state gives a compact field map for every durable file; add --full for formal JSON Schemas, and --yaml (before or after the subcommand) for YAML output. find auto-detects plain component names, exact hierarchy paths, and wildcard globs. A slash-free glob searches component names at any hierarchy depth; use --name NAME for an exact display name and --kind regex only when regular-expression matching is intentional. The same auto-detection resolves the component arguments of display, mark, render-sheet, and occlusion, so a glob there fans out to every match just as find does (occlusion '**/*' measures the whole scene). Component paths remain authoritative when scene.json reports hierarchy: flattened; intermediate non-mesh source nodes stay encoded in the path even though they are not separately addressable components.

Install

MeshProbe uses Python 3.12 or newer, uv, and Blender 5.2 LTS on Linux and Windows. The qualification harness uses Bubblewrap on Linux. On Ubuntu 24.04 or newer, load the distro's AppArmor profile. It grants Bubblewrap the namespace permissions needed by the harness and keeps user-namespace hardening enabled:

sudo apt-get install apparmor-profiles bubblewrap
sudo cp \
  /usr/share/apparmor/extra-profiles/bwrap-userns-restrict \
  /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict

Windows needs no additional sandbox package. MeshProbe creates a fresh AppContainer profile for each episode and puts the agent in a constrained Job Object. Install the pinned Blender release from an elevated PowerShell prompt:

choco install blender --version=5.2.0 --yes

Install the locked Python environment and run the test suite:

uv sync
uv run meshprobe schema
uv run pytest

Sessions and lifecycle commands

MeshProbe stores session data in the current project's .meshprobe directory by default. Use --workspace to choose another project root and -s/--session to switch sessions.

The lifecycle commands have deliberately different failure and persistence semantics:

Command Renderer shutdown Checkpoint retained Daemon
close Graceful, selected session Yes Keeps running
close --all Graceful, every session Yes Stops gracefully
kill Immediate, selected session Last acknowledged state Keeps running
kill --all Immediate process-tree stop Last acknowledged state Force-stops
delete-data Requires a stopped daemon Deletes all .meshprobe data Stays stopped

Use close at the end of ordinary work. Use kill to test recovery or escape a wedged renderer. Neither command deletes the session: the next inspection operation reopens the source, verifies its hash, and replays the checkpoint. delete-data is the explicit cleanup operation.

Each session contains:

.meshprobe/sessions/<name>/
  metadata.json       # source, status, worker PID
  scene.json          # complete scene manifest
  components.yml      # deterministic c1, c2, ... reference index
  state.yml           # camera, lighting, sparse display/mark overrides
  checkpoint.json     # acknowledged replay log
  events.jsonl        # accepted and rejected operations
  results/ snapshots/ artifacts/

The CLI and MCP adapter share the same Pydantic operation contracts. Blender remains a separate factory-clean worker, and source files stay untouched. Renders, manifests, traces, and normalized cache entries are derived artifacts.

MCP clients get the same discriminated command schema and result envelope through one meshprobe tool. Start the stdio server with:

uv run meshprobe-mcp

Qualification harness

The evaluation harness runs each agent without network access, with a read-only assigned model, a writable artifact directory, and evaluator-owned traces and render passes. It uses Bubblewrap on Linux and AppContainer plus Job Object limits on Windows. It supports a JSONL command-line protocol and an MCP stdio protocol. Both adapters use the same prompt, command schema, budgets, broker, and answer contract.

Build the released 512-model procedural corpus and the 160-model curated track, then combine and pin them:

uv run meshprobe eval generate .corpora --version procedural-v7
uv run meshprobe eval curated-generate \
  evals/curated/catalog.json .cache/meshprobe-curated .corpora \
  --build-version curated-v2 --corpus-version curated-tasks-v7
uv run meshprobe eval merge .corpora \
  .corpora/procedural-v7 .corpora/curated-tasks-v7 \
  --version qualification-v8
uv run meshprobe eval pin \
  .corpora/qualification-v8 .corpora/manifests-v8

The resulting release corpus has 672 models, 2,528 episodes, and 672 full-stack investigations. The nested smoke, pull-request, nightly, and release manifests committed under evals/manifests/public/ pin every episode, model, corpus, MeshProbe version, Blender version, importer, and render engine. The curated catalog pins the source commit, download hash, topology hash, license, and attribution for 20 CC0 assets; the build creates eight controlled variants of each three-source inspection assembly.

The manifests committed under evals/manifests/public/ pin qualification-v8, built under the current 2576²/1200² render-resolution defaults; running the commands above reproduces it exactly.

Run a pinned tier with either agent transport:

uv run meshprobe eval run-tier \
  .corpora/qualification-v8 evals/manifests/public/smoke.json .runs \
  --adapter cli \
  --blender /path/to/blender \
  --agent-command-json '["/path/to/agent"]'

The CLI agent reads one episode envelope, sends tool_call lines, and finishes with a structured submission. With --adapter mcp, the agent speaks MCP as a client over its standard streams and calls the meshprobe and submit tools. Runs checkpoint after each episode and publish JSON plus Markdown reports split by operation, task family, difficulty, projection, focal-length band, illumination, model source, and failure gate. On Linux the episode envelope uses /workspace/input and /workspace/artifacts paths; on Windows it uses the assigned absolute paths granted to the AppContainer.

To test the packaged wheel instead of the checkout, run the pinned smoke tier through a clean uv environment:

uv run python tools/clean_install_smoke.py \
  .corpora/qualification-v8 evals/manifests/public/smoke.json \
  .runs/clean-install-smoke

The command records the wheel hash, runtime versions, manifest hash, and qualification report hash in clean-install-report.json.

To migrate existing local datasets without regenerating geometry or task identities:

uv run meshprobe eval migrate .corpora/procedural-v5 .corpora --version procedural-v6
uv run meshprobe eval migrate .corpora/curated-tasks-v5 .corpora \
  --version curated-tasks-v6
uv run meshprobe eval migrate .corpora/qualification-v6 .corpora \
  --version qualification-v7
uv run meshprobe eval migrate .corpora/private-v7 .corpora \
  --version private-v8 --opaque-family opaque_family_v8

eval migrate only accepts a schema-1 or schema-2 source (it upgrades the manifest shape itself); qualification-v7 and its inputs are already schema-3, so migrating past them into the new -v7/-v8 names above is not this command's job. To land on procedural-v7, curated-tasks-v7, or qualification-v8 instead, regenerate a fresh corpus with eval generate/eval curated-generate/eval merge/eval pin (the release-corpus commands earlier in this section) rather than eval migrate.

eval audit-migration verifies that model hashes, episode IDs, evaluator truth, and the full-investigation operation contract survived the migration. Schema-v1 corpora are rejected by the qualification runtime instead of being translated silently.

Every qualification agent runs in the platform sandbox and retains its exact prompt, live protocol stream, and stderr under episodes/<episode>/evaluator/agent-run/. The same audit artifacts are retained by the ergonomics pilot.

The separate CLI ergonomics pilot pairs 12 basic and 12 intermediate episodes across Claude Opus and Codex Luna (gpt-5.6-luna). It records exposed command trajectories and token accounting; it does not claim access to hidden chain of thought and does not replace release qualification. The harness silently enforces a 768,000-token ceiling on each attempt in addition to its wall-clock limit. On Linux, agents run inside Bubblewrap with only the assigned public model, a clean MeshProbe install, credentials, and their writable attempt directory mounted. Blender is mounted read-only as a runtime dependency. The repository and private evaluator data stay outside the sandbox:

uv run meshprobe eval ergonomics \
  .corpora/qualification-v7 .runs/ergonomics-v1

Live provider streams and stderr are saved beneath .runs/ergonomics-v1/attempts/<episode>/<agent>/attempt-<n>/; the exact prompt is saved there as prompt.txt. The ergonomics pilot currently runs on Linux; Windows support will use AppContainer isolation and will not fall back to an unsandboxed agent.

The approved session and migration design is in docs/session-cli-plan.md. The broader qualification design remains in docs/plan.md.

Release notes are recorded in CHANGELOG.md.

License

Apache-2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

meshprobe-0.4.0.tar.gz (2.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

meshprobe-0.4.0-py3-none-any.whl (242.2 kB view details)

Uploaded Python 3

File details

Details for the file meshprobe-0.4.0.tar.gz.

File metadata

  • Download URL: meshprobe-0.4.0.tar.gz
  • Upload date:
  • Size: 2.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for meshprobe-0.4.0.tar.gz
Algorithm Hash digest
SHA256 ed7f22297c8c6e8a7e789aea9dbf5d7f0aa9d0dbc6005cf9923b751dbe6c31a4
MD5 a102e573b31d55abc2f8778591ce6321
BLAKE2b-256 989beb207627a858a0b2215f6e0bbec7ce7bcc0912ef36224fd4e1d40e085df3

See more details on using hashes here.

File details

Details for the file meshprobe-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: meshprobe-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 242.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for meshprobe-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9fc231d0029af440f77985c456bda37e9e6599895ec682ccb550c57de6edb397
MD5 dbbc9b10bdba5676b8b6958f3b4cada7
BLAKE2b-256 7db3b77e26470db50740a6c28f8947ca62aa1d01bb8ada45e34ba8e49a926c0e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page