Cross-DCC Verification
Prove an asset produced by one DCC through its MCP server can be consumed, inspected, and validated by a second DCC. This page explains the contract
dcc-mcp-coreships and how downstream DCC repositories plug into it.
Why a contract, not an implementation
Every DCC (Blender, Maya, Unreal, Photoshop, Houdini, ...) has its own native import API, its own scene traversal primitives, and its own idea of what counts as a "mesh". dcc-mcp-core stays out of that jungle on purpose: it only defines the shape of a verifier's result and the skill template every downstream implementation is expected to clone. Keeping the shape tiny (three fields) is what makes the contract portable.
The actual import_and_inspect logic lives in the downstream repos:
| DCC | Repository | Skill name (convention) |
|---|---|---|
| Blender | dcc-mcp-blender | blender-fbx-verifier |
| Maya | dcc-mcp-maya | maya-fbx-verifier |
| Unreal | dcc-mcp-unreal | unreal-fbx-verifier |
| Photoshop | dcc-mcp-photoshop | photoshop-psd-verifier |
The SceneStats contract
from dcc_mcp_core import SceneStats
observed = SceneStats(
object_count=1,
vertex_count=482,
has_mesh=True,
extra={"dcc": "blender-3.6"}, # optional DCC-specific enrichments
)| Field | Type | Meaning |
|---|---|---|
object_count | int | Top-level objects seen after import. |
vertex_count | int | Total vertex count across all mesh geometry. |
has_mesh | bool | True when any imported object is polygon geometry. |
extra | dict | Free-form enrichments. Survives serialisation, ignored by the core comparison. |
Comparing producer vs verifier
The helper SceneStats.matches() implements the only comparison semantics core endorses:
- Strict on
object_count(structural invariant). - Strict on
has_mesh(detects silent empty imports). - Fuzzy on
vertex_count(±5% by default) because FBX normals, UV seams, and tangent basis changes can split vertices on re-import.
produced = SceneStats(object_count=1, vertex_count=482, has_mesh=True)
observed = verifier_skill__import_and_inspect("/tmp/sphere.fbx")
assert produced.matches(observed, vertex_tolerance=0.05), (
f"round-trip drift: expected {produced}, got {observed}"
)Extra fields beyond the three core ones belong in extra. The comparator does not read extra, which keeps DCC-specific telemetry from ever breaking a round-trip assertion.
Writing a verifier skill
Scaffold a skill with dcc-mcp-skills-creator, then wire it to dcc_mcp_core.SceneStats. To create a verifier for a new DCC:
- Scaffold a new skill directory in your downstream repo (e.g.
dcc-mcp-blender/skills/blender-fbx-verifier/). - Edit
SKILL.md: setdcc: blender(or similar) and rename the skill to match your repo's convention. - Replace the stub body of
scripts/import_and_inspect.pywith your DCC's native import + inspection calls. Return aSceneStats.to_dict()payload wrapped byskill_success(...). - Add a CI job in your downstream repo that:
- Boots two DCC processes (or one producer + one verifier).
- Runs a producer skill to create and export an asset.
- Runs your new verifier skill on the exported file.
- Asserts
produced.matches(observed)usingdcc_mcp_core.SceneStats.
Example stub (Blender)
import bpy
from dcc_mcp_core import SceneStats
from dcc_mcp_core.skill import skill_entry, skill_success
def main(params):
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.fbx(filepath=params["file_path"])
objects = list(bpy.context.scene.objects)
meshes = [o for o in objects if o.type == "MESH"]
vertex_count = sum(len(m.data.vertices) for m in meshes)
stats = SceneStats(
object_count=len(objects),
vertex_count=vertex_count,
has_mesh=bool(meshes),
extra={"blender_version": bpy.app.version_string},
)
return skill_success("Imported and inspected", **stats.to_dict())
if __name__ == "__main__":
skill_entry(main)Where the round-trip CI lives
dcc-mcp-core does not ship a Blender-with-FBX round-trip job. That belongs in whichever downstream repo owns the producer or verifier binary, because only those repos pin the DCC version matrix. The invariant core asserts is shape-only, exercised by tests/test_verifier_contract.py.
Downstream repos following this contract should add a CI job along these lines:
1. Start producer DCC in headless mode, load producer skill.
2. mcpcall call: producer__create_sphere, producer__export_fbx.
3. Start verifier DCC in headless mode, load verifier skill.
4. mcpcall call: verifier__import_and_inspect(/tmp/sphere.fbx).
5. Python assertion: SceneStats.from_dict(...).matches(produced).FAQ
Why only three fields? What about bounding boxes, materials, animations?
Every field that enters the core contract has to mean the same thing across every DCC, forever. Three fields (objects / vertices / has-mesh) is the set where we could rigorously define and test the semantics. Anything DCC-specific (materials, cameras, animation frame counts, bounding boxes) can travel through extra without committing core to a cross-DCC definition.
Can I extend SceneStats in a downstream repo?
Do not subclass. Put downstream-specific data in extra. If a field becomes genuinely universal across all supported DCCs we'll promote it to the core contract in a minor release.
What if my DCC doesn't expose vertex counts?
Return vertex_count=0 and set an explanatory note in extra. Round- trip tests that target that DCC should then pass has_mesh as the only real assertion.
Related
dcc-thread-safety.md— main-thread dispatcher primitives that verifier skills rely on when running inside a live DCC session.host-adapter.md— theHostAdapterbase class downstream repos extend to plug a verifier into their DCC's idle loop.skills.md— SKILL.md format the verifier template is built on.