# chuk-mcp-stage
> **3D Scene & Camera MCP Server** - The director layer between physics simulation and motion rendering
[](https://www.python.org/downloads/)
[](https://modelcontextprotocol.io)
[](https://docs.python.org/3/library/asyncio.html)
**π Quick Install:** `uvx stage.chukai.io/mcp`
**chuk-mcp-stage** is the orchestration layer that bridges:
- **chuk-mcp-physics** (Rapier simulations) β Scene animations
- **chuk-motion / Remotion** (video rendering) β Export targets
It's the **"director + set designer"** that defines what's in the 3D world, where the camera goes, and how physics drives motion.
---
## π― What This Does
**Core capabilities:**
1. **Scene Graph** - Define 3D worlds (objects, materials, lighting)
2. **Camera Paths** - Cinematography (orbit, chase, dolly, static shots)
3. **Physics Bridge** - Bind scene objects to physics bodies (uses public Rapier service by default)
4. **Animation Baking** - Convert physics simulations β keyframes
5. **Export** - Generate R3F components, Remotion projects, glTF
**The full pipeline:**
```
Physics Simulation β Stage β Motion/Video
(chuk-mcp-physics) β (chuk-mcp-stage) β (chuk-motion/Remotion)
```
---
## π Quick Start
### Installation
**Option 1: Install from public URL (Recommended)**
```bash
# Install directly from public URL with uvx
uvx stage.chukai.io/mcp
```
**Option 2: Install from PyPI**
```bash
pip install chuk-mcp-stage
```
**Option 3: Install from source**
```bash
cd chuk-mcp-stage
pip install -e .
```
**Physics ready out-of-the-box!** Uses the public Rapier service at `https://rapier.chukai.io` by default. No additional setup required for physics simulations.
### Run the Server
```bash
# STDIO mode (default - for MCP clients like Claude Desktop)
uv run chuk-mcp-stage
# HTTP mode (REST API on port 8000)
uv run chuk-mcp-stage http
# Streamable mode (Server-Sent Events for streaming responses)
uv run chuk-mcp-stage streamable
```
**Transport modes:**
- **stdio**: Standard MCP protocol via stdin/stdout (default for Claude Desktop)
- **http**: HTTP REST API server on port 8000 (used by chuk-mcp-r3f-preview)
- **streamable**: SSE (Server-Sent Events) transport for streaming responses
π **[Complete Transport Modes Guide](docs/TRANSPORT_MODES.md)** - Detailed documentation, examples, and troubleshooting
### Configure in Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
**Option 1: Using public URL (Recommended)**
```json
{
"mcpServers": {
"stage": {
"command": "uvx",
"args": ["stage.chukai.io/mcp"],
"env": {
"RAPIER_SERVICE_URL": "https://rapier.chukai.io"
}
}
}
}
```
**Option 2: Using local installation**
```json
{
"mcpServers": {
"stage": {
"command": "chuk-mcp-stage",
"env": {
"RAPIER_SERVICE_URL": "https://rapier.chukai.io"
}
}
}
}
```
### Physics Integration Configuration
chuk-mcp-stage integrates with physics simulations via **[chuk-mcp-physics](https://github.com/chrishayuk/chuk-mcp-physics)** and the Rapier physics engine.
**Three Integration Methods:**
1. **Direct Rapier HTTP** (Default) - Fastest for simulations
- Directly calls Rapier service HTTP API
- Used by `stage_bake_simulation` tool
- Defaults to public service: `https://rapier.chukai.io`
2. **Via MCP Tools** - Most flexible
- Use chuk-mcp-physics MCP server tools
- Supports both analytic calculations and Rapier simulations
- Requires chuk-mcp-physics server running
3. **Hybrid** - Best of both worlds
- Use MCP tools for simulation creation/setup
- Use direct HTTP for baking trajectories
**Environment Variables:**
```bash
# Rapier service URL (default: https://rapier.chukai.io)
RAPIER_SERVICE_URL=https://rapier.chukai.io # Public service
# RAPIER_SERVICE_URL=http://localhost:9000 # Local development
# Rapier timeout in seconds (default: 30.0)
RAPIER_TIMEOUT=30.0
# Physics provider type (default: auto)
PHYSICS_PROVIDER=auto # or 'rapier', 'mcp'
```
**Claude Desktop with Custom Rapier Service:**
```json
{
"mcpServers": {
"stage": {
"command": "chuk-mcp-stage",
"env": {
"RAPIER_SERVICE_URL": "http://localhost:9000",
"RAPIER_TIMEOUT": "60.0"
}
},
"physics": {
"command": "uvx",
"args": ["chuk-mcp-physics"],
"env": {
"RAPIER_SERVICE_URL": "http://localhost:9000"
}
}
}
}
```
**Public Rapier Service:**
- URL: `https://rapier.chukai.io`
- No authentication required
- Rate limits may apply
- Perfect for prototyping and demos
**Local Rapier Service:**
```bash
# Run locally with Docker
docker run -p 9000:9000 chuk-rapier-service
# Or from source
cd rapier-service && cargo run --release
```
See **[chuk-mcp-physics README](https://github.com/chrishayuk/chuk-mcp-physics)** for complete physics integration guide.
**What's New in chuk-mcp-physics v0.3.1:**
- **52 physics tools** (expanded from 27) - Now covers ~50% of common physics use cases
- **Rotational dynamics**: Torque, moment of inertia, angular momentum calculations
- **Springs & oscillations**: Simple harmonic motion, damped oscillations, pendulums
- **Circular motion**: Orbital mechanics, centripetal force, escape velocity
- **Advanced collisions**: 3D elastic/inelastic collisions with coefficient of restitution
- **Conservation laws**: Energy and momentum verification for simulations
- **Fluid dynamics**: Drag, buoyancy, terminal velocity, underwater motion
---
### Google Drive OAuth Storage (HTTP Mode)
**Store your scenes in Google Drive** with OAuth 2.1 authentication for secure, persistent, user-owned storage!
When running in **HTTP mode**, chuk-mcp-stage supports Google Drive OAuth integration. Users authenticate via their browser, and scenes are stored in their own Google Drive under `/CHUK/stage/`.
**Benefits:**
- β
**Secure OAuth 2.1** - Industry-standard authentication with PKCE
- β
**User Owns Data** - Scenes stored in user's Google Drive, not your infrastructure
- β
**Auto Token Refresh** - Seamless authentication with automatic refresh
- β
**Cross-Device Access** - Access scenes from any device with Drive
- β
**Built-in Sharing** - Share scenes using Google Drive's native sharing
- β
**Natural Discoverability** - View/edit scene files directly in Drive UI
- β
**No Infrastructure Cost** - Zero storage costs for the provider
#### Setup Steps
**1. Create Google Cloud Project:**
- Go to https://console.cloud.google.com/
- Create new project (or select existing)
- Enable **Google Drive API**
- Go to **OAuth consent screen**:
- User Type: External
- Add your email as test user
- Go to **Credentials** β Create **OAuth 2.0 Client ID**:
- Application type: Web application
- Authorized redirect URIs: `http://localhost:8000/oauth/callback`
- Copy Client ID and Client Secret
**2. Install with Google Drive Support:**
```bash
pip install "chuk-mcp-stage[google_drive]"
```
**3. Configure Environment:**
```bash
# Copy example environment file
cp .env.example .env
# Edit .env and add your Google credentials:
# GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
# GOOGLE_CLIENT_SECRET=your-client-secret
```
**4. Verify OAuth Integration (Optional but Recommended):**
```bash
# Verify that OAuth setup works
python examples/verify_google_drive_oauth.py
```
This will verify:
- OAuth provider initializes correctly
- Credentials are valid
- OAuth endpoints can be registered
- Ready for Google Drive integration
**5. Run Server in HTTP Mode:**
```bash
# Load from .env file
uv run chuk-mcp-stage http
# Or set environment variables directly
export GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="your-client-secret"
uv run chuk-mcp-stage http
```
**6. Authorize Access:**
When Claude Desktop (or any MCP client) connects:
1. OAuth flow automatically initiates
2. Browser opens for Google authorization
3. User grants access to Google Drive
4. Tokens securely stored and auto-refreshed
**OAuth Endpoints** (automatically registered):
- Authorization: `http://localhost:8000/oauth/authorize`
- Token: `http://localhost:8000/oauth/token`
- Discovery: `http://localhost:8000/.well-known/oauth-authorization-server`
- Callback: `http://localhost:8000/oauth/callback`
#### Deployment to Fly.io
**β
OAuth is now configured for https://stage.chukai.io and https://physics.chukai.io**
See [OAUTH_SETUP_COMPLETE.md](OAUTH_SETUP_COMPLETE.md) for details on the production OAuth setup.
For production deployments, set secrets instead of using `.env`:
```bash
# Set Google OAuth credentials as Fly secrets
fly secrets set GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
fly secrets set GOOGLE_CLIENT_SECRET="your-client-secret"
# Set OAuth server URL (use your Fly.io app URL)
fly secrets set OAUTH_SERVER_URL="https://your-app.fly.dev"
fly secrets set GOOGLE_REDIRECT_URI="https://your-app.fly.dev/oauth/callback"
# Optional: Configure session backend for production
fly secrets set SESSION_PROVIDER="redis"
fly secrets set SESSION_REDIS_URL="redis://your-redis-url:6379/0"
# Deploy
fly deploy
```
**Important**: Update Google Cloud Console with production redirect URI:
- Add `https://your-app.fly.dev/oauth/callback` to authorized redirect URIs
#### Storage Providers
chuk-mcp-stage supports multiple storage backends - see **[STORAGE_CONFIGURATION.md](STORAGE_CONFIGURATION.md)** for complete details.
**Quick Comparison**:
| Provider | Persistence | Cloud Sync | OAuth Required | Setup | Best For |
|----------|-------------|------------|----------------|-------|----------|
| **vfs-filesystem** (default) | β
Local | β | β | Zero | Local dev |
| **vfs-filesystem + OAuth** | β
Persistent | β
Google Drive | β
| Medium | Production (small) |
| **vfs-s3** | β
Persistent | β
S3 | β | Medium | Production (large) |
| **vfs-memory** | β RAM only | β | β | Zero | Testing only |
**Environment Variables**:
```bash
# Storage provider selection (default: vfs-filesystem)
STORAGE_PROVIDER=vfs-filesystem
# Session metadata storage (default: memory)
SESSION_PROVIDER=memory
# For Redis sessions (production)
SESSION_PROVIDER=redis
REDIS_URL=redis://localhost:6379/0
# For AWS S3 storage
STORAGE_PROVIDER=vfs-s3
AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=xxx
AWS_S3_BUCKET=chuk-artifacts
AWS_REGION=us-east-1
```
See **[STORAGE_CONFIGURATION.md](STORAGE_CONFIGURATION.md)** for:
- Detailed provider comparison
- Migration guides
- Production best practices
- Troubleshooting
#### Where Your Scenes Live
**With vfs-filesystem (default)**:
```
~/.chuk-artifacts/
βββ grid/
βββ {sandbox_id}/
βββ {session_id}/
βββ {namespace_id}/
βββ scene.json
βββ animations/
βββ export/
```
**With Google Drive (vfs-filesystem + OAuth)**:
```
Google Drive
βββ chuk-artifacts/
βββ {user_id}/
βββ {namespace_id}/
βββ scene.json
βββ animations/
β βββ cannonball.json
βββ export/
βββ remotion/
```
**With AWS S3 (vfs-s3)**:
```
s3://your-bucket/
βββ grid/
βββ {sandbox_id}/
βββ {session_id}/
βββ {namespace_id}/
βββ scene.json
βββ animations/
```
**Storage Scope Behavior**:
- **SESSION scope** (unauthenticated) β Local filesystem only, ephemeral
- **USER scope** (authenticated) β Google Drive (if OAuth enabled) or S3, persistent
---
## π¦ Tool Surface
### Scene Management
```python
# Create a new scene
stage_create_scene(name, author, description)
# Add 3D objects
stage_add_object(
scene_id,
object_id,
object_type, # "box", "sphere", "cylinder", "plane"
position_x, position_y, position_z,
radius, size_x, size_y, size_z,
material_preset, # "metal-dark", "glass-blue", "plastic-white"
color_r, color_g, color_b
)
# Set environment & lighting
stage_set_environment(
scene_id,
environment_type, # "gradient", "solid", "hdri"
lighting_preset # "three-point", "studio", "noon"
)
```
### Camera & Shots
```python
# Add camera shot
stage_add_shot(
scene_id,
shot_id,
camera_mode, # "orbit", "static", "chase", "dolly"
start_time,
end_time,
focus_object, # Object to orbit/chase
orbit_radius,
orbit_elevation,
orbit_speed,
easing # "ease-in-out-cubic", "spring", "linear"
)
# Get shot details
stage_get_shot(scene_id, shot_id)
```
### Physics Integration
```python
# Bind object to physics body
stage_bind_physics(
scene_id,
object_id,
physics_body_id # "rapier://sim-abc/body-ball"
)
# Bake simulation to keyframes
stage_bake_simulation(
scene_id,
simulation_id,
fps=60,
duration=10.0,
physics_server_url=None # Optional: defaults to https://rapier.chukai.io
)
```
### Export
```python
# Export to R3F/Remotion/glTF
stage_export_scene(
scene_id,
format, # "r3f-component", "remotion-project", "gltf", "json"
output_path
)
# Get complete scene data
stage_get_scene(scene_id)
```
---
## π§© Core Concepts
### Stage Objects
A **Stage Object** is an entry in the scene graph that represents a 3D visual element. Every object has:
| Property | Description | Example |
|----------|-------------|---------|
| **id** | Unique identifier | `"ball"`, `"ground"`, `"car-chassis"` |
| **type** | Primitive shape | `"sphere"`, `"box"`, `"cylinder"`, `"plane"` |
| **transform** | Position, rotation, scale | `{position: [0, 5, 0], rotation: [0, 0, 0], scale: [1, 1, 1]}` |
| **material** | Visual appearance | `"glass-blue"`, `"metal-dark"`, custom PBR |
| **physics_binding** | Optional physics link | `"rapier://sim-abc/body-ball"` |
**Example scene JSON:**
```json
{
"id": "demo-scene",
"name": "Falling Ball Demo",
"objects": {
"ground": {
"id": "ground",
"type": "plane",
"transform": {
"position": {"x": 0, "y": 0, "z": 0},
"rotation": {"x": 0, "y": 0, "z": 0},
"scale": {"x": 1, "y": 1, "z": 1}
},
"size": {"x": 20, "y": 20, "z": 1},
"material": {
"preset": "metal-dark"
},
"physics_binding": null
},
"ball": {
"id": "ball",
"type": "sphere",
"transform": {
"position": {"x": 0, "y": 5, "z": 0},
"rotation": {"x": 0, "y": 0, "z": 0},
"scale": {"x": 1, "y": 1, "z": 1}
},
"radius": 1.0,
"material": {
"preset": "glass-blue",
"color": {"r": 0.3, "g": 0.6, "b": 1.0}
},
"physics_binding": "rapier://sim-falling/body-ball"
}
},
"shots": {
"main": {
"id": "main",
"camera_path": {
"mode": "orbit",
"focus": "ball",
"radius": 8.0,
"elevation": 30.0
},
"start_time": 0.0,
"end_time": 10.0
}
}
}
```
**Why this matters for LLMs:**
When you create an object with `stage_add_object`, you're modifying this scene graph. Later operations like `stage_bind_physics` or `stage_add_shot` reference the **same object ID** you created. This makes it easy to reason about: "I want to modify the ball I just created" β just use `object_id="ball"`.
### Authoring vs Baking
chuk-mcp-stage has **two distinct phases**:
#### 1οΈβ£ Authoring Phase (Define the World)
**What you're doing:** Planning and composing the scene
**Operations:**
- Create scene structure
- Place objects (primitives, positions, materials)
- Define camera shots and movements
- Bind object IDs to physics body IDs
- Set environment and lighting
**Output:** Scene definition (metadata only, no animation yet)
**Tools used:**
- `stage_create_scene`
- `stage_add_object`
- `stage_add_shot`
- `stage_bind_physics`
- `stage_set_environment`
#### 2οΈβ£ Baking Phase (Generate Animation Data)
**What you're doing:** Converting physics simulation to renderable keyframes
**Operations:**
- Connect to physics simulation (Rapier)
- Sample physics state at desired FPS
- Convert body positions/rotations β keyframes
- Store animation data in scene VFS
**Output:** Timestamped keyframe arrays (position, rotation, velocity per frame)
**Tools used:**
- `stage_bake_simulation` (connects to physics, generates keyframes)
- `stage_export_scene` (exports scene + baked animations)
#### Typical Flow
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AUTHORING PHASE β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 1. stage_create_scene(name="demo") β
β β Creates empty scene graph β
β β
β 2. stage_add_object(id="ground", type="plane", ...) β
β stage_add_object(id="ball", type="sphere", y=10, ...) β
β β Defines visual objects (no motion yet) β
β β
β 3. stage_set_environment(lighting="three-point") β
β β Sets lights, background β
β β
β 4. Use physics MCP to create simulation β
β create_simulation(gravity_y=-9.81) β
β add_rigid_body(sim_id, body_id="ball", ...) β
β β Physics oracle creates simulation β
β β
β 5. stage_bind_physics(object_id="ball", β
β body_id="rapier://sim-id/body-ball") β
β β Links visual object to physics body β
β β
β 6. step_simulation(sim_id, steps=600) # 10s @ 60 FPS β
β β Physics oracle runs simulation β
β β
β 7. stage_add_shot(mode="orbit", focus="ball", ...) β
β β Defines camera cinematography β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β BAKING PHASE β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 8. stage_bake_simulation(scene_id, sim_id, fps=60, dur=10) β
β β Fetches physics data from Rapier β
β β Converts to keyframes β
β β Stores in /animations/ball.json β
β β
β 9. stage_export_scene(format="remotion-project") β
β β Generates R3F/Remotion code β
β β Includes baked animation data β
β β Returns artifact URIs β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Authoring = "What should exist and where?"
Baking = "What motion actually happened?"
```
**Key Insight:** Authoring is **declarative** (you define intent), baking is **computational** (physics oracle generates the motion).
---
## π¬ Example Workflow
### 1. Simple Falling Ball Demo
```python
# 1. Create scene
scene = await stage_create_scene(
name="falling-ball-demo",
description="Ball falling under gravity"
)
# 2. Add ground plane
await stage_add_object(
scene_id=scene.scene_id,
object_id="ground",
object_type="plane",
size_x=20.0,
size_y=20.0,
material_preset="metal-dark"
)
# 3. Add falling ball
await stage_add_object(
scene_id=scene.scene_id,
object_id="ball",
object_type="sphere",
radius=1.0,
position_y=5.0,
material_preset="glass-blue",
color_r=0.3,
color_g=0.5,
color_b=1.0
)
# 4. Add orbiting camera shot
await stage_add_shot(
scene_id=scene.scene_id,
shot_id="orbit-shot",
camera_mode="orbit",
focus_object="ball",
orbit_radius=8.0,
orbit_elevation=30.0,
orbit_speed=0.1,
start_time=0.0,
end_time=10.0
)
# 5. Export to Remotion
result = await stage_export_scene(
scene_id=scene.scene_id,
format="remotion-project"
)
```
### 2. Physics-Driven Animation
**Note:** This example uses the **public Rapier service** (`https://rapier.chukai.io`) by default. No configuration needed!
```python
# 1. Create physics simulation (chuk-mcp-physics)
sim = await create_simulation(gravity_y=-9.81)
await add_rigid_body(
sim_id=sim.sim_id,
body_id="ball",
body_type="dynamic",
shape="sphere",
radius=1.0,
position=[0, 5, 0]
)
# 2. Create scene
scene = await stage_create_scene(name="physics-demo")
await stage_add_object(
scene_id=scene.scene_id,
object_id="ball",
object_type="sphere",
radius=1.0,
position_y=5.0
)
# 3. Bind physics to visual
await stage_bind_physics(
scene_id=scene.scene_id,
object_id="ball",
physics_body_id=f"rapier://{sim.sim_id}/body-ball"
)
# 4. Run simulation (chuk-mcp-physics)
await step_simulation(sim_id=sim.sim_id, steps=600)
# 5. Bake physics β keyframes
await stage_bake_simulation(
scene_id=scene.scene_id,
simulation_id=sim.sim_id,
fps=60,
duration=10.0
)
# 6. Export with animation data
await stage_export_scene(
scene_id=scene.scene_id,
format="r3f-component"
)
```
---
## π Examples
The `examples/` directory contains ready-to-run demonstrations of all features.
### π Start Here: Golden Path
**The canonical example showing the complete pipeline:**
```bash
uv run examples/00_golden_path_ball_throw.py
```
This example demonstrates:
- β
**Authoring phase** - Scene creation, object placement, camera shots
- β
**Baking phase** - Physics simulation β keyframes (conceptual)
- β
**Export phase** - Generate R3F/Remotion code
- β
**Artifact URIs** - How chuk-mcp-stage integrates with chuk-artifacts
- β
**Two-phase model** - Declarative β Computational
- β
**Complete pipeline** - Physics β Stage β Motion β Video
**This is the best example to understand the CHUK stack cohesion.**
### Getting Started
```bash
# Run any example
uv run examples/00_golden_path_ball_throw.py # β Start here!
uv run examples/01_simple_scene.py
uv run examples/02_physics_integration_demo.py
uv run examples/03_camera_shots_demo.py
uv run examples/04_export_formats.py
uv run examples/05_full_physics_workflow.py
```
### Example Guide
| Example | Purpose | What You'll Learn |
|---------|---------|-------------------|
| **00_golden_path_ball_throw.py** β | **Complete pipeline** | **Full workflow, artifact URIs, two-phase model** |
| **01_simple_scene.py** | Basic scene creation | Objects, transforms, materials, simple camera |
| **02_physics_integration_demo.py** | Physics binding concepts | Binding objects to physics bodies, metadata |
| **03_camera_shots_demo.py** | Camera cinematography | ORBIT, STATIC, DOLLY, CHASE modes, easing functions |
| **04_export_formats.py** | Export capabilities | JSON, R3F, Remotion, glTF formats and use cases |
| **05_full_physics_workflow.py** | Complete pipeline | Full physics-to-video workflow with public Rapier |
### Example Outputs
**00_golden_path_ball_throw.py** β - Complete pipeline demonstration
```
π Artifact URIs (Not file contents!):
Scene data: artifact://stage/golden-path-ball-throw/exports/scene.json
R3F: artifact://stage/golden-path-ball-throw/exports/r3f/Scene.tsx
Remotion: artifact://stage/golden-path-ball-throw/exports/remotion/
π¬ Complete Pipeline:
1. Authoring - Define scene structure β
2. Physics - Create simulation (conceptual)
3. Binding - Link objects β bodies β
4. Baking - Physics β keyframes (conceptual)
5. Export - Scene β R3F/Remotion β
6. Render - Remotion β MP4 (external)
```
**01_simple_scene.py** - Creates falling ball scene
```
β Created scene: falling-ball
β Added ground plane
β Added ball at (0, 5, 0)
β Added orbit camera shot (10s)
```
**03_camera_shots_demo.py** - 38-second multi-shot sequence
```
πΉ Shot Sequence:
β’ 0.0s - 10.0s ORBIT - Smooth orbit around center
β’ 10.0s - 15.0s STATIC - Static wide angle
β’ 15.0s - 22.0s DOLLY - Dolly tracking shot
β’ 22.0s - 28.0s CHASE - Chase with spring easing
β’ 28.0s - 33.0s ORBIT - Fast linear orbit
β’ 33.0s - 38.0s STATIC - Low angle hero shot
```
**04_export_formats.py** - Exports to all formats
```
β JSON: /exports/scene.json
β R3F: /exports/r3f/Scene.tsx
β Remotion: /exports/remotion/Root.tsx
β glTF: /exports/scene.gltf
Note: In production, these would be artifact URIs like:
artifact://stage/{scene_id}/exports/scene.json
```
**05_full_physics_workflow.py** - Shows complete pipeline
```
π¬ Complete Pipeline:
1. Physics Simulation (chuk-mcp-physics)
2. Scene Composition (chuk-mcp-stage) β
3. Bind Physics β
4. Bake Simulation (Rapier service)
5. Export (R3F/Remotion) β
6. Render Video (Remotion)
```
### Learning Path
**Recommended order:**
1. **Start with `00_golden_path_ball_throw.py`** β - See the complete pipeline first
2. Understand basics with `01_simple_scene.py`
3. Explore camera control with `03_camera_shots_demo.py`
4. Learn export options with `04_export_formats.py`
5. See physics concepts with `02_physics_integration_demo.py`
6. Complete workflow with `05_full_physics_workflow.py`
**Why start with golden path?** It shows you the **destination** (full pipeline) before diving into individual pieces. You'll understand how all the tools work together in the CHUK stack.
---
## ποΈ Architecture
### Scene Storage
- **Backend**: chuk-artifacts (VFS-backed workspaces)
- **Format**: JSON scene definitions with nested objects
- **Scope**: SESSION (ephemeral), USER (persistent), SANDBOX (shared)
Each scene is a workspace containing:
```
/scene.json # Scene definition
/animations/ # Baked keyframe data
ball.json
car.json
/export/ # Generated R3F/Remotion code
r3f/
remotion/
```
### Camera Path Modes
| Mode | Use Case | Parameters |
|------|----------|------------|
| `orbit` | Product shots, inspection | radius, elevation, speed, focus |
| `static` | Fixed observation | position, look_at |
| `chase` | Follow moving objects | target, offset, damping |
| `dolly` | Linear reveals | from_position, to_position, look_at |
| `flythrough` | Scene tours | waypoints[] |
| `crane` | Cinematic sweeps | pivot, arc, height_range |
### Material Presets
- `metal-dark`, `metal-light`
- `glass-clear`, `glass-blue`, `glass-green`
- `plastic-red`, `plastic-blue`, `plastic-white`
- `rubber-black`
- `wood-oak`
### Export Formats
- **R3F Component** - React Three Fiber `.tsx` files
- **Remotion Project** - Full project with `package.json`
- **glTF** - Static 3D scene file
- **JSON** - Raw scene data
### VFS & Artifacts Integration
**chuk-mcp-stage** is tightly integrated with **[chuk-artifacts](https://github.com/chrishayuk/chuk-artifacts)** and **[chuk-virtual-fs](https://github.com/chrishayuk/chuk-virtual-fs)** for storage and asset management.
#### Why This Matters
Unlike typical MCP servers that return large JSON blobs inline, chuk-mcp-stage returns **artifact URIs**:
```python
# β Traditional approach (bloated)
{
"scene_data": "...<10MB of JSON>...",
"r3f_component": "...<5000 lines of TSX>...",
"animations": "...<50MB of keyframes>..."
}
# β
CHUK approach (cohesive)
{
"scene": "artifact://stage/demo-scene/scene.json",
"component": "artifact://stage/demo-scene/export/r3f/Scene.tsx",
"animations": "artifact://stage/demo-scene/animations/ball.json"
}
```
**Benefits:**
1. **No inline bloat** - Tools return URIs, not massive data
2. **Persistent storage** - Scenes survive across sessions (if using USER scope)
3. **VFS operations** - Use `vfs_ls`, `vfs_find`, `vfs_cp` to manage scene files
4. **Cross-tool sharing** - Other MCP servers can access same artifacts
5. **Checkpoint support** - Version control for scene iterations
#### Storage Model
Each scene = **one workspace** in chuk-artifacts:
```
artifact://stage/{scene_id}/
βββ scene.json # Scene definition
βββ animations/ # Baked physics keyframes
β βββ ball.json # Per-object animation data
β βββ car.json
β βββ character.json
βββ export/ # Generated code
βββ r3f/ # React Three Fiber
β βββ Scene.tsx
β βββ Camera.tsx
β βββ animations.json
βββ remotion/ # Remotion project
β βββ Composition.tsx
β βββ Root.tsx
β βββ package.json
βββ gltf/ # 3D model exports
βββ scene.gltf
```
#### Example: Working with Artifacts
```python
# 1. Create scene (returns artifact URI)
result = await stage_create_scene(name="demo")
# β {"scene_id": "demo-xyz", "workspace": "artifact://stage/demo-xyz"}
# 2. Add objects and bake simulation
# ... (authoring phase)
# 3. Export to Remotion (returns artifact URIs)
export_result = await stage_export_scene(
scene_id="demo-xyz",
format="remotion-project",
output_path="/export/remotion"
)
# β {
# "composition": "artifact://stage/demo-xyz/export/remotion/Composition.tsx",
# "root": "artifact://stage/demo-xyz/export/remotion/Root.tsx",
# "package": "artifact://stage/demo-xyz/export/remotion/package.json"
# }
# 4. Use VFS tools to explore (via chuk-virtual-fs MCP)
await vfs_ls("artifact://stage/demo-xyz/export/remotion")
# β ["Composition.tsx", "Root.tsx", "package.json"]
await vfs_read("artifact://stage/demo-xyz/export/remotion/package.json")
# β Returns package.json contents
# 5. Copy to another location
await vfs_cp(
"artifact://stage/demo-xyz/export/remotion",
"artifact://projects/my-video"
)
```
#### Integration with Other CHUK Tools
**chuk-mcp-r3f-preview** can directly preview scenes:
```python
# Stage creates scene
scene_uri = "artifact://stage/demo-xyz/export/r3f/Scene.tsx"
# R3F preview server loads it
await r3f_preview_scene(scene_uri)
# β Opens interactive 3D preview in browser
```
**chuk-motion** can render baked animations:
```python
# Stage bakes physics
animation_uri = "artifact://stage/demo-xyz/animations/ball.json"
# Motion applies spring physics to keyframes
await motion_apply_spring(animation_uri, stiffness=100)
```
This is where the **CHUK stack cohesion** shines: Every tool speaks the same artifact URI language.
---
## π Integration with CHUK Stack
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β chuk-mcp-stage β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββ β
β β Scene Graph β β Camera β β Physics Bridge β β
β β β β Paths β β β β
β ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββββ¬ββββββββββ β
βββββββββββΌββββββββββββββββββΌββββββββββββββββββββΌβββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββ βββββββββββββββββββββββ
β chuk-artifacts β β chuk-motion β β chuk-mcp-physics β
β β β β β (Rapier) β
β β’ scene.json β β β’ easing β β β
β β’ assets/ β β β’ springs β β β’ rigid bodies β
β β’ animations/ β β β’ keyframes β β β’ constraints β
βββββββββββββββββββ ββββββββ¬βββββββ β β’ sim state β
β βββββββββββββββββββββββ
βΌ
βββββββββββββββββββ
β Remotion β
β β
β β’ R3F render β
β β’ video export β
β β’ MP4 output β
βββββββββββββββββββ
```
---
## π― Use Cases
### Immediate Wins
1. **Physics Explainer Videos** - Auto-generate educational content
2. **Simulation-as-a-Service** - LLMs can request visualizations
3. **Procedural B-Roll** - Synthetic motion graphics
### Vertical Plays
4. **Motorsport Visualization** - Racing lines, braking zones, overtakes
5. **3D Data Storytelling** - Animated datasets with cinematography
6. **Science Journalism** - Render model predictions visually
### Weird & Powerful
7. **Explainable AI Animations** - Show what models are thinking
8. **Virtual Physics Lab** - Programmable experiments
9. **Agent Cinematography** - AI chooses camera paths
---
## π Data Models
All models are **Pydantic-native** with **no dictionary goop**:
```python
from chuk_mcp_stage.models import (
Scene, # Complete scene definition
SceneObject, # 3D object (mesh, material, transform)
Shot, # Camera path + time range
CameraPath, # Camera movement definition
Material, # PBR material properties
Environment, # Lighting & background
BakedAnimation, # Physics β keyframes
)
```
**Enums everywhere:**
```python
ObjectType.SPHERE
MaterialPreset.GLASS_BLUE
CameraPathMode.ORBIT
LightingPreset.THREE_POINT
ExportFormat.R3F_COMPONENT
```
---
## π§ͺ Testing
```bash
# Run tests
pytest
# With coverage
pytest --cov=chuk_mcp_stage
```
---
## π οΈ Development
```bash
# Install dev dependencies
pip install -e ".[dev]"
# Format
black src/ tests/
# Lint
ruff check src/
# Type check
mypy src/
```
---
## π License
MIT License - see LICENSE for details
---
## π Why This Matters
Most people can:
β
Run simulations
β
Generate charts
β
Animate text
Almost nobody can:
> **Simulate β Direct β Render β Explain β Export**
**chuk-mcp-stage gives you that pipeline.**
You're not rendering things anymore.
You're **producing explainable simulations as media**.
---
**Built with β€οΈ for the CHUK AI stack**