Skip to main content

Quickstart — server

Building a client instead? See Quickstart (client) →.

1 — Install

pip install motionmcp-sdk

Requires Python 3.10+. Pulls FastAPI, uvicorn, Pydantic, numpy.

2 — Try it without writing any code

The package ships with a NullBackbone that returns the rest pose for any request. It exists so DCC plugin authors can develop against a real server with zero ML dependencies.

python -m motionmcp.null_backbone
# → MMCP server on :8000

In another shell:

curl -s http://localhost:8000/capabilities | jq

That's a working MMCP server. No model required.

3 — Write your own server

The whole thing is one Backbone subclass:

server.py
import numpy as np
from motionmcp import (
Backbone, ModelSpec, GenerateRequest,
MotionResult, Skeleton, Joint, serve,
)

class MyBackbone(Backbone):
def capabilities(self) -> ModelSpec:
return ModelSpec(
id="my-model",
fps=30.0,
canonical_skeleton=Skeleton(joints=[
Joint(name="Hips", parent=None,
rest_translation=(0.0, 0.95, 0.0),
rest_rotation=(0.0, 0.0, 0.0, 1.0)),
]),
supports_retargeting=False,
supported_constraints=["pose_keyframe"],
)

async def generate(self, req: GenerateRequest) -> MotionResult:
# Replace this with: rotations, root_translations = self.model.run(req)
N = req.options.num_samples if req.options else 1
T = req.total_frames
J = len(req.skeleton.joints)

rotations = np.zeros((N, T, J, 4), dtype=np.float32)
rotations[..., 3] = 1.0 # identity quat (x,y,z,w)
root = np.zeros((N, T, 3), dtype=np.float32)

return MotionResult(
rotations=rotations,
root_translations=root,
)

if __name__ == "__main__":
serve(MyBackbone())

Run it:

python server.py

Hit it:

curl -s http://localhost:8000/generate \
-H 'Content-Type: application/json' \
-d '{
"protocol_version": "1.0",
"model": "my-model",
"skeleton": {
"joints": [
{ "name": "Hips", "parent": null,
"rest_translation": [0, 0.95, 0],
"rest_rotation": [0, 0, 0, 1] }
]
},
"segments": [{ "type": "text", "prompt": "stand", "duration_frames": 30 }]
}' \
> motion.gltf

The response is a standard glTF 2.0 document.

What the SDK is doing for you

Behind that single generate() call:

  • Parsed and validated the JSON against the MMCP wire schema.
  • Verified protocol_version, looked up the model, and ran retargeting policy.
  • Checked every constraint references known joints and frames in range.
  • Encoded your numpy arrays as glTF bufferViews and accessors.
  • Built the extensions.MMCP_motion block (fps, sample names, foot contacts, chunk boundaries).
  • Caught any ProtocolError you raise and turned it into the MMCP error envelope with the right HTTP status.

If you'd written that yourself it would be ~300 lines.

What's next

  • Backbone reference → — the full surface of ModelSpec, MotionResult, sync vs async, hooks.
  • Errors → — the typed exceptions you raise so the SDK returns the correct error envelope.
  • Serving → — multi-model servers, custom FastAPI mounting, deployment.
  • Concepts → — what skeletons, segments, constraints, and timing actually mean. Useful when designing your generate() implementation.