Skip to content

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-core ships 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:

DCCRepositorySkill name (convention)
Blenderdcc-mcp-blenderblender-fbx-verifier
Mayadcc-mcp-mayamaya-fbx-verifier
Unrealdcc-mcp-unrealunreal-fbx-verifier
Photoshopdcc-mcp-photoshopphotoshop-psd-verifier

The SceneStats contract ​

python
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
)
FieldTypeMeaning
object_countintTop-level objects seen after import.
vertex_countintTotal vertex count across all mesh geometry.
has_meshboolTrue when any imported object is polygon geometry.
extradictFree-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.
python
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.

Deterministic capture responses ​

Capture implementations can use dcc_mcp_core.runtime.capture_contract to publish one bounded response shape:

python
from dcc_mcp_core import CaptureTargetSpec, build_capture_response

response = build_capture_response(
    frame.data,
    width=frame.width,
    height=frame.height,
    format="png",
    backend="mock",
    target=CaptureTargetSpec.active_viewport(),
    return_mode="artifact_only",  # image or both are explicit opt-ins
    unchanged_if_hash=previous_sha256,
)

artifact_only is the compatibility-safe default and returns an artefact://sha256/... reference plus bounded metadata (dimensions, bytes, sha256, format, backend, and target) without inline pixels. image adds one base64 image payload; both adds that payload alongside the artifact reference. A matching unchanged_if_hash suppresses image data and marks the response unchanged: true. Targets are explicit (dcc_window, active_viewport, scene_viewer, or a positive bounded crop); hidden or unknown targets fail closed.

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:

  1. Scaffold a new skill directory in your downstream repo (e.g. dcc-mcp-blender/skills/blender-fbx-verifier/).
  2. Edit SKILL.md: set dcc: blender (or similar) and rename the skill to match your repo's convention.
  3. Replace the stub body of scripts/import_and_inspect.py with your DCC's native import + inspection calls. Return a SceneStats.to_dict() payload wrapped by skill_success(...).
  4. 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) using dcc_mcp_core.SceneStats.

Example stub (Blender) ​

python
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.

  • dcc-thread-safety.md — main-thread dispatcher primitives that verifier skills rely on when running inside a live DCC session.
  • host-adapter.md — the HostAdapter base 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.

Released under the MIT License.