DSP-Geometry-Engine
An LLM cannot, inside its context window, traverse a 75k-vertex adjacency array, unwrap and
differentiate a phase spectrum, integrate a BRDF over the hemisphere, or take the rank of a
controllability matrix. It can read a one-line verdict. This repository is that translation layer:
MCP tools that do the mechanical math on data already sitting on disk and hand back a compact JSON
summary — a number, a boolean, a ranked list — small enough for a model to reason about. Arrays never
cross the MCP boundary; the full spectra, plots, models, and images stay under data/.
It ships two independent MCP servers. dsp-geometry-engine is a DSP microscope over mesh dumps,
renders, and telemetry (49 tools in 11 packs). blender-body-mesh is a local Blender/MPFB
character-generation server (6 tools). They are separate processes with separate tool lists; the
client, not the servers, moves artifacts between them.
The private C++ renderer is not required. Exactly one of the 49 DSP tools shells out to it
(extract_mesh_telemetry, src/dsp_server/toolsets/geometry.py:107 — the only engine_cli call site
in the tree). The other 48 read .ply, .csv, .npz, .json, and image files off disk and are pure
Python. A bundled stub CLI covers even that one: see
Run it without the engine.
Status
v0.1.0. 348 tests, all passing, none of which need the C++ engine. The corrugation RCA that motivated the project is concluded: the forearm ripple is 118–123 cycles/meter (~8.4 mm, the mesh edge-loop spacing, ~20 dB prominence), it is present identically in the source mesh's bind positions, and the renderer's capsule welds, flexion deformers, and retargeted weights were exonerated — the defect originates upstream in a Blender retarget/bake step. The tools outlived the bug they were built for.
Requirements
dsp-geometry-engine — Python 3.12 and uv. That is the whole list.
Every dependency is a pure-wheel install (numpy, scipy, matplotlib, pillow, sympy, scikit-learn,
pywavelets, mcp[cli]); there is no torch and no model weights to download — even the perceptual
FR-VQA pack is pure scipy. Optionally DSP_ENGINE_ROOT pointing at a build of the separate, private
C++ renderer, which unlocks extract_mesh_telemetry against real engine dumps.
blender-body-mesh — additionally needs, and none of these are bundled here:
| Needed | Why |
|---|---|
| Windows | Every path default in src/bodymesh_server/config.py is a Windows location; all are env-overridable, but the lane has only been exercised there. |
Blender 4.2 (blender.exe, never blender-launcher.exe) |
The worker scripts run inside it. |
| MPFB 2.0.x, installed as a Blender extension | The only supported body backend. MB-LAB is reported by discovery and never invoked. |
skeleton_male.json / skeleton_female.json |
The 55-bone engine-canonical rig the retarget targets. Set BODYMESH_ENGINE_SKELETON_DIR. |
A built character_bake_cli.exe |
Bakes a completed candidate into an engine character. Set BODYMESH_CHARACTER_BAKE_EXE. |
inspect_bodymesh_runtime is a zero-cost pre-flight for exactly this: it creates nothing, launches
nothing, and names every missing artifact by absolute path (Blender executable is missing…,
MPFB 2.0.x was not detected…, engine skeleton assets are missing: <paths>,
character_bake_cli is missing: <path>). Run it first — see
Command-line entrypoints.
Quickstart
uv sync # install (Python 3.12, locked deps)
uv run pytest # full suite — runs against the bundled stub engine, no C++ needed
Run it without the engine
tests/stub_engine.py honors the real dump CLI's argument grammar, exit codes, and stderr protocol,
and writes a 64×16 synthetic forearm cylinder with a 60 cycles/meter ripple injected at 2 mm
amplitude (tests/stub_engine.py:26-27). Pointing DSP_ENGINE_CLI at it exercises discovery, feature
detection, teeing, and stderr parsing end to end — the whole engine path minus the C++.
The tools' implementations are plain module-level functions; the MCP server and the tests both call
them 1:1, so an example needs no MCP client. Do not start dsp-server by hand for this.
# 1. Point the bridge at the stub. Absolute, because the bridge — not your shell —
# resolves this path, and a .py suffix means "run through the current interpreter".
$env:DSP_ENGINE_CLI = "$PWD/tests/stub_engine.py"
# 2. Dump a posed mesh.
uv run python -c "from dsp_server.toolsets import AppContext, geometry; ctx = AppContext.from_config(); print(geometry._extract_mesh_telemetry(ctx, pose_clip='clip-cin-walktalk', label='demo'))"
Real output, abridged (absolute paths shortened, bone_map/stderr_tail/engine_stale dropped):
{"ply_path":"…/data/dumps/demo_clip-cin-walktalk_20260731-151117.ply",
"meta_path":"…/data/dumps/demo_clip-cin-walktalk_20260731-151117.meta.json",
"palette_path":"…/data/dumps/demo_clip-cin-walktalk_20260731-151117.palette.json",
"duration_s":0.203,"vertex_count":1024,"face_count":2016,"bone_count":11,
"clip_id":"clip-cin-walktalk","sample_time":0.0,"collision_push":0.0012,
"baked_collision_push":0.0,"imported":false,"has_weights":true,
"arm_chain_histogram":{"armUpperL (4)":16,"armLowerL (5)":992,"handL (6)":16}}
Now feed that PLY to the analysis half — this tool never touches the engine, it only reads the file:
# 3. Analyze the forearm segment (substitute the ply_path printed above).
uv run python -c "from dsp_server.toolsets import AppContext, geometry; ctx = AppContext.from_config(); print(geometry._analyze_corrugation(ctx, dump=r'data/dumps/demo_clip-cin-walktalk_20260731-151117.ply', joint='armLowerL'))"
Real output, abridged (dump_path, diff1_var, diff2_var, plot_path, and four of the five
spectral_peaks dropped):
{"joint":"armLowerL","joint_ids":[5],"channel":"posed","vertex_count":992,"n_samples":256,
"t_span_m":0.217407,
"roughness":{"rms_ripple_m":0.00123165,"rel_ripple":0.0307911,"hf_energy_ratio":0.998741,
"dominant_freq_cpm":59.5622,"dominant_wavelength_m":0.0167892,
"peak_prominence_db":45.7819,"ridge_count_est":13,
"sector_phase_coherence":1.0,"corrugated":true},
"spectral_peaks":[{"freq_cpm":59.5622,"prominence_db":45.767}],
"extent_t_start_m":-0.108703,"extent_t_end_m":0.108704,"extent_band_energy_fraction":0.914806}
dominant_freq_cpm 59.5622 recovers the 60 cycles/meter the stub injected, at 45.8 dB prominence,
verdict corrugated: true. That closed loop — synthesize a known defect, measure it back — is what
the 48 file-reading tools do against real data.
On Windows,
uv runre-syncs the virtualenv and will fail withos error 32if a registered MCP stdio server is holding.venv/Scripts/*.exe. Useuv run --no-sync …in that situation.
MCP registration
The client registrations are not tracked — they contain machine-specific absolute paths and a committed copy fails hard on any clone. Copy the templates and edit them:
Copy-Item .mcp.json.example .mcp.json # Claude Code
Copy-Item .codex/config.toml.example .codex/config.toml # Codex
Then edit the paths. In .mcp.json, also delete the _comment key — strict JSON has no comment
syntax. Both templates document every environment variable inline; every one is optional, and
deleting a line falls back to the in-code default. If you have no Blender/MPFB install, delete the
whole blender-body-mesh entry — the DSP server is unaffected. For Claude Desktop, run
scripts/register-claude-desktop.ps1, which derives the repo root from its own location and merges
both entries into claude_desktop_config.json without clobbering sibling servers.
After adding or changing a registration, restart the client and open a fresh task in this project.
Never start dsp-server or bodymesh-server by hand inside a session that already registered
them — an extra stdio instance sits waiting for JSON-RPC on a pipe nobody is writing to and looks
hung.
Two MCP servers, one client workflow
Their registered names and executable entrypoints are different:
| Registered MCP name | Executable | Responsibility | Current runtime |
|---|---|---|---|
dsp-geometry-engine |
dsp-server |
DSP, imaging, geometry, engine telemetry, and the other analysis tool packs | Local stdio or streamable HTTP (authenticate when exposed) |
blender-body-mesh |
bodymesh-server |
Private-photo job preparation, Blender/MPFB body generation, engine rigging, character baking, and identity renders | Local stdio only |
Some diagnostics may also show the internal FastMCP display titles DSP-Geometry-Engine and
Blender-BodyMesh. The lowercase names in the table are the canonical client registration keys.
Loading both registrations in Codex or Claude does not combine them into one process. The MCP client starts and talks to each server separately. They have separate tool lists, configuration, subprocesses, logs, failure boundaries, and deployment requirements.
flowchart LR
Client["Codex or Claude<br/>MCP client"]
Body["blender-body-mesh<br/>bodymesh-server"]
DSP["dsp-geometry-engine<br/>dsp-server"]
Files[("MCP-owned artifact files")]
Client -->|"body-mesh tool call"| Body
Body -->|"writes GLB, baked JSON, PLY, masks, identity PNGs"| Files
Client -->|"passes an artifact path"| DSP
Files -->|"reads the selected artifact"| DSP
DSP -->|"compact analysis result"| Client
There is no MCP-to-MCP call between the two servers. The client performs the orchestration: it calls one server, receives artifact paths, and supplies a selected path to a tool on the other server.
The body-mesh MCP creates bounded MPFB candidates from explicit parameters, retains front/side image
references, renders comparison silhouettes, and exports both neutral DSP telemetry and an engine-ready
55-bone arms-down GLB/baked character with skin weights and tangents. For a completed candidate it can
also render the fixed eight-closeup/three-body-view identity-v1 set without changing the candidate
contract. It does not claim one-shot photo reconstruction — fitting a body to photographs is an
iterative parametric approximation, and a single clothed image is underdetermined. See
docs/BODY-MESH-MCP.md.
Tool packs
The following 49 tools and 11 packs belong only to dsp-geometry-engine; they are not part of
blender-body-mesh. The packs cover the engine's own DSP lane, one pack per relevant EE course, a
rendering lane for the ray tracer, a video lane for the AI-video comparison gate, and a perceptual
FR-VQA lane. Each pack registers by default; trim per client with the DSP_TOOLSETS environment
variable (comma-separated names). General data goes in as
.csv/.tsv/.json/.npz/.npy paths (column-addressed); .ply paths address engine dumps as
column="<joint>[:posed|rest]"; video tools take a directory or list of frame images.
Pack (DSP_TOOLSETS name) |
Course / lane | Tools |
|---|---|---|
geometry |
ELE407 DSP + mesh QA | extract_mesh_telemetry, analyze_corrugation, compare_geometry_signals, localize_defect, lbs_differential, score_bake, analyze_mesh_topology |
imaging |
ELE490 Image Processing | compare_depth_renders, enhance_image, filter_image, segment_image, restore_image, compare_wavelet_signatures |
stats |
ELE320 Probability & Statistics | describe, fit_distribution, hypothesis_test, regression_fit, compare_dump_ripples |
engmath |
MAT235/236 Engineering Math | solve_ode, laplace_transform, linear_algebra, residues_and_integrals, fourier_series |
systems |
ELE301 Signals & Systems + control | lti_response, pole_zero, bode, sampling_check, convolve_signals, group_delay, state_space_analysis |
ml |
ELE489 Machine Learning | feature_engineer_dump, cluster, reduce_dims, classify_eval, predict |
netqueue |
ELE412 Data Communication | queueing_calc, little_law, erlang_blocking |
os |
Operating Systems (Tanenbaum) | schedule_sim, page_replacement_sim, bankers_check |
rendering |
PBR / ray-tracer energy | verify_brdf_energy |
video |
AI-video comparison gate | evaluate_spatiotemporal_frequencies, verify_motion_consistency, verify_camera_projection, analyze_photometric_consistency, evaluate_occlusion_boundaries |
perceptual |
Perceptual / semantic FR-VQA (classical) | evaluate_perceptual_similarity, verify_identity_coherence |
All tools return small JSON summaries; arrays, plots, models, and images stay on disk under data/
(plots/, series/, images/, features/, models/, brdf/, video/, perceptual/). Preset examples:
DSP_TOOLSETS=geometry,imaging,stats for a corrugation-RCA session; DSP_TOOLSETS=engmath,systems,stats
for coursework-style calculation; DSP_TOOLSETS=geometry,rendering for engine mesh + shader QA;
DSP_TOOLSETS=video,imaging,geometry,perceptual for the full AI-video generation comparison gate
(math + perceptual).
Command-line entrypoints
Two console scripts ([project.scripts] in pyproject.toml) run a single tool without an MCP client,
for callers that spawn helpers and parse JSON. Both print the tool's exact JSON envelope on stdout.
dsp-tool exposes the comparison-gate subset of the DSP tools over the same _impl functions the MCP
server registers. Naming a tool it does not carry prints the roster:
uv run dsp-tool no_such_tool
{"error": "unknown tool 'no_such_tool'", "hint": "available: ['analyze_photometric_consistency', 'compare_wavelet_signatures', 'evaluate_occlusion_boundaries', 'evaluate_perceptual_similarity', 'evaluate_spatiotemporal_frequencies', 'score_bake', 'verify_camera_projection', 'verify_identity_coherence', 'verify_motion_consistency']"}
A real call takes the tool's arguments as one JSON object. Exit code 0 means the tool ran — a
tool-level error is still valid JSON on stdout, carrying error and a hint; exit 2 means an unknown
tool or malformed arguments:
uv run dsp-tool verify_motion_consistency --args-json '{\"frames\": \"data/video/no-such-clip\"}'
{"error":"VideoError: no frames matched 'data/video/no-such-clip' (pass a directory, a glob, or a path list)","hint":"pass a directory of same-size frames or an explicit path list (>= 2 frames); grid_step/window control the flow grid and Lucas-Kanade window size","stderr_tail":null}
(The backslash-escaped quotes are a Windows PowerShell 5.1 requirement — it strips inner double quotes
when handing an argument to a native executable, and the CLI then rejects the JSON. POSIX shells take
'{"frames": "..."}' verbatim.) See docs/COMPARISON-GATE.md for what the
gate tools measure and how their verdicts combine.
bodymesh-tool exposes all 6 body-mesh tools the same way. The pre-flight needs no arguments and
touches nothing:
uv run bodymesh-tool inspect_bodymesh_runtime
Real output from a fully provisioned machine, abridged (absolute paths shortened, blender_shortcut
and the MB-LAB addons entry dropped, MPFB's enabled_hint/path dropped, its note truncated at
the ellipsis):
{"blender_executable":"C:/Program Files/Blender Foundation/Blender 4.2/blender.exe",
"blender_exists":true,"data_dir":"…/data/bodymesh","input_roots":["…/data/bodymesh/inbox"],
"engine_skeleton_dir":"…","character_bake_executable":"…/character_bake_cli.exe",
"engine_contract_available":true,
"addons":[{"backend":"mpfb","installed":true,"version":"2.0.16","supported":true,
"note":"Primary backend: the only body generator this MCP invokes, driven headlessly
through the confined Blender worker. Licensing of MPFB, its bundled assets, and
anything generated with them is the operator's to determine …"}],
"supported_backend":"mpfb","execution_mode":"local-stdio/background-subprocess","warnings":[]}
The server makes no claim about MPFB's own terms — determine them from the installed extension.
On a machine without Blender, MPFB, the skeleton JSONs, or the bake exe, the same call returns
blender_exists: false / engine_contract_available: false and one warnings entry per missing
artifact, each naming the path it looked at.
Docs
docs/ENGINE-PLAYBOOK.md— when/where to reach for each tool in an engine-debugging session (advisory).docs/COMPARISON-GATE.md— the AI-video comparison gate: symptom→tool, how the per-tool verdicts combine into one clip pass/fail, and the honest caveats (proven end-to-end intests/test_comparison_gate_e2e.py).docs/ARCHITECTURE.md— dual-server topology, related repositories, tool surfaces, and isolation logic.docs/BODY-MESH-MCP.md— separate Blender server, cross-server handoff, rig contract, and troubleshooting.docs/DEVELOPMENT.md— setup, lint/test, engine build lane, MCP registration, adding a course pack.docs/DEPLOYMENT.md— DSP-server-only container/AWS deployment, env var reference, and security.LICENSES/README.md— the directory-scoped license map and why the Apache/GPL split holds.llms.txt— canonical rules for LLM agents working in this repo (imported byCLAUDE.md).
License
Apache-2.0, with one directory-scoped carve-out: everything under
src/bodymesh_server/blender_scripts/** is GPL-3.0-or-later, because those files execute inside
Blender and reach MPFB's GPL service API. The distribution as a whole is
Apache-2.0 AND GPL-3.0-or-later, which is the SPDX expression in pyproject.toml.
The split holds because the boundary is a process boundary, not a link: bodymesh_server launches the
workers via subprocess.Popen(argv, shell=False) and only request/result JSON and filesystem paths
cross. No worker imports a project module, and MPFB is reached through importlib.import_module at
runtime. Neither Blender nor MPFB is bundled here or in any wheel built from this repository.
See LICENSE,
NOTICE, and
LICENSES/README.md for the
full map, the third-party dependency notes, and the copyright statement.
Copyright 2026 hgner hgner09@gmail.com.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dsp_geometry_engine-0.1.0.tar.gz.
File metadata
- Download URL: dsp_geometry_engine-0.1.0.tar.gz
- Upload date:
- Size: 544.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
146fee3d62b1cf02601839c73615ca8f7a19a812e68b4fad250cecd61d156f76
|
|
| MD5 |
0c2b36d859216b156fbfb731afb9c438
|
|
| BLAKE2b-256 |
fa53b1c6ef6fc70c124383bb3b0fec1585eab6d722c970cfa5e9f88e653ee3be
|
File details
Details for the file dsp_geometry_engine-0.1.0-py3-none-any.whl.
File metadata
- Download URL: dsp_geometry_engine-0.1.0-py3-none-any.whl
- Upload date:
- Size: 312.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
798e6035c6dde497c80cc5bc98a040dd6c343817e650107468ccc2f035250a64
|
|
| MD5 |
44fee9230d57a7859c350192558726c7
|
|
| BLAKE2b-256 |
1f7ce07683a44008f47782b77163814a4a46dc758736f3f422af79d9b89a1594
|