A comprehensive solution for integrating ManimGL (Mathematical Animation Engine) with Cursor's Model Context Protocol (MCP), featuring automatic directory management, animation history tracking, and replay functionality.
- 🚀 Automatic Directory Management - Creates and manages
scripts/manim/
andvisualizations/manim/
folders - 📜 Animation History Tracking - Logs every animation with timestamp, script, scene, and arguments
- 🔄 Replay System - Re-run any previous animation by ID
- 🎯 Organized Output - Clean separation of scripts and rendered content
- 🎨 Preview Window - Maintains ManimGL's interactive preview functionality
- 🔧 MCP Integration - Seamless integration with Cursor's AI coding assistant
- Python 3.8+ with ManimGL installed (
pip install manimgl
) - Cursor IDE with MCP support
- Windows PowerShell (for installer script)
- Download and run the PowerShell installer:
# Download installer (replace with your gist URL)
Invoke-WebRequest -Uri "https://gist.githubusercontent.com/[USERNAME]/[GIST_ID]/raw/setup_manimgl_mcp.ps1" -OutFile "setup_manimgl_mcp.ps1"
# Run installer
.\setup_manimgl_mcp.ps1
- Create Directory Structure:
mkdir -p scripts/manim
mkdir -p visualizations/manim
- Create Wrapper Script (save as
manimgl_wrapper.py
):
#!/usr/bin/env python3
"""
ManimGL Wrapper Script for MCP
Ensures proper directory structure exists before running ManimGL
Includes history tracking and replay functionality
"""
import os
import sys
import subprocess
import json
import datetime
from pathlib import Path
class ManimHistory:
def __init__(self, scripts_dir):
self.scripts_dir = Path(scripts_dir)
self.history_file = self.scripts_dir / ".manim_history.json"
self.history = self.load_history()
def load_history(self):
"""Load animation history from file"""
if self.history_file.exists():
try:
with open(self.history_file, 'r') as f:
return json.load(f)
except:
return []
return []
def save_history(self):
"""Save animation history to file"""
with open(self.history_file, 'w') as f:
json.dump(self.history, f, indent=2)
def add_entry(self, script_file, scene_name, args, working_dir):
"""Add a new entry to history"""
entry = {
"timestamp": datetime.datetime.now().isoformat(),
"script_file": str(script_file),
"scene_name": scene_name,
"args": args,
"working_dir": str(working_dir),
"id": len(self.history) + 1
}
self.history.append(entry)
self.save_history()
return entry["id"]
def list_history(self, limit=10):
"""List recent animation history"""
print("📜 Recent ManimGL Animation History:")
print("=" * 60)
if not self.history:
print("No previous animations found.")
return
recent = self.history[-limit:] if len(self.history) > limit else self.history
for entry in reversed(recent):
timestamp = datetime.datetime.fromisoformat(entry["timestamp"])
print(f"[{entry['id']:2d}] {timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
print(f" 📄 {entry['script_file']}")
print(f" 🎬 {entry['scene_name']}")
if entry.get('args'):
args_str = " ".join(entry['args'])
if len(args_str) > 50:
args_str = args_str[:47] + "..."
print(f" ⚙️ {args_str}")
print()
def get_entry(self, entry_id):
"""Get a specific history entry by ID"""
for entry in self.history:
if entry["id"] == entry_id:
return entry
return None
def replay_entry(self, entry_id):
"""Replay a specific animation by ID"""
entry = self.get_entry(entry_id)
if not entry:
print(f"❌ Animation with ID {entry_id} not found.")
return False
print(f"🔄 Replaying animation #{entry_id}:")
print(f" 📄 {entry['script_file']}")
print(f" 🎬 {entry['scene_name']}")
# Change to the working directory
os.chdir(entry["working_dir"])
# Build the command
cmd = [sys.executable, "-m", "manimlib"] + entry["args"]
print(f"✓ Running: {' '.join(cmd)}")
result = subprocess.run(cmd)
return result.returncode == 0
def ensure_directories():
"""Create the required directory structure if it doesn't exist"""
# Get the project root (where this script is located)
project_root = Path.cwd()
# Define the required directories
scripts_dir = project_root / "scripts" / "manim"
visualizations_dir = project_root / "visualizations" / "manim"
# Create directories if they don't exist
scripts_dir.mkdir(parents=True, exist_ok=True)
visualizations_dir.mkdir(parents=True, exist_ok=True)
print(f"✓ Scripts directory: {scripts_dir}")
print(f"✓ Visualizations directory: {visualizations_dir}")
return scripts_dir, visualizations_dir
def show_help():
"""Show custom help for the wrapper"""
print("ManimGL Wrapper - Enhanced with History")
print("=" * 40)
print()
print("Usage:")
print(" python manimgl_wrapper.py [script.py] [SceneName] [options]")
print(" python manimgl_wrapper.py --history [--limit N]")
print(" python manimgl_wrapper.py --replay ID")
print()
print("History Commands:")
print(" --history Show recent animation history")
print(" --history --limit N Show last N animations (default: 10)")
print(" --replay ID Replay animation with given ID")
print()
print("Examples:")
print(" manimgl_wrapper.py fibonacci_spiral.py FibonacciSpiral")
print(" manimgl_wrapper.py --history")
print(" manimgl_wrapper.py --replay 5")
print()
print("All other ManimGL options are passed through:")
def main():
"""Main wrapper function"""
try:
# Ensure directories exist first
scripts_dir, visualizations_dir = ensure_directories()
# Initialize history with scripts directory
history = ManimHistory(scripts_dir)
# Handle history commands
if "--history" in sys.argv:
limit = 10
if "--limit" in sys.argv:
try:
limit_idx = sys.argv.index("--limit") + 1
limit = int(sys.argv[limit_idx])
except (IndexError, ValueError):
limit = 10
history.list_history(limit)
return
if "--replay" in sys.argv:
try:
replay_idx = sys.argv.index("--replay") + 1
entry_id = int(sys.argv[replay_idx])
success = history.replay_entry(entry_id)
sys.exit(0 if success else 1)
except (IndexError, ValueError):
print("❌ Please provide a valid animation ID to replay.")
print("Use --history to see available animations.")
sys.exit(1)
if "--help" in sys.argv or "-h" in sys.argv:
show_help()
print()
print("ManimGL Help:")
print("-" * 40)
# Also show ManimGL help
subprocess.run([sys.executable, "-m", "manimlib", "--help"])
return
# Normal ManimGL execution
# Change to scripts directory
os.chdir(scripts_dir)
print(f"✓ Working directory: {scripts_dir}")
# Build the ManimGL command
manimgl_args = [
sys.executable, "-m", "manimlib"
]
# Add the video_dir argument
manimgl_args.extend(["--video_dir", str(visualizations_dir)])
# Add any additional arguments passed to this wrapper
user_args = []
if len(sys.argv) > 1:
user_args = sys.argv[1:]
manimgl_args.extend(user_args)
print(f"✓ Running: {' '.join(manimgl_args)}")
# Extract script file and scene name for history
script_file = ""
scene_name = ""
if len(user_args) >= 1:
script_file = user_args[0]
if len(user_args) >= 2:
scene_name = user_args[1]
# Add to history if we have both script and scene
if script_file and scene_name and not any(arg.startswith('-') for arg in [script_file, scene_name]):
entry_id = history.add_entry(script_file, scene_name, user_args, scripts_dir)
print(f"📝 Added to history as #{entry_id}")
# Execute ManimGL
result = subprocess.run(manimgl_args)
sys.exit(result.returncode)
except Exception as e:
print(f"❌ Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
- Update MCP Configuration - Add to your
~/.cursor/mcp.json
:
{
"mcpServers": {
"manimgl": {
"command": "python",
"args": ["path/to/your/project/manimgl_wrapper.py"],
"cwd": "path/to/your/project",
"env": {
"MANIMGL_LOG_LEVEL": "INFO"
}
}
}
}
After setup, your project will have:
PROJECT_ROOT/
├── scripts/
│ └── manim/ # 📁 All ManimGL Python scripts
│ ├── .manim_history.json # 📝 Animation execution history
│ └── [your_scripts.py] # 🎨 Your animation scripts
├── visualizations/
│ └── manim/ # 📁 All ManimGL outputs
│ ├── [output_videos.mp4] # 🎥 Rendered videos
│ └── [output_images.png] # 🖼️ Rendered images
└── manimgl_wrapper.py # 🔧 Enhanced wrapper with history
Simply ask your AI assistant to create ManimGL animations, and everything is handled automatically!
View Animation History:
python manimgl_wrapper.py --history
python manimgl_wrapper.py --history --limit 20 # Show last 20
Replay Previous Animation:
python manimgl_wrapper.py --replay 5 # Replay animation #5
Run New Animation:
python manimgl_wrapper.py my_script.py MyScene
python manimgl_wrapper.py my_script.py MyScene --skip_animations
Get Help:
python manimgl_wrapper.py --help
Each animation run creates a history entry with:
- ID: Unique identifier for replay
- Timestamp: When the animation was executed
- Script File: Which Python file was run
- Scene Name: Which scene class was rendered
- Arguments: Complete command line arguments used
MANIMGL_LOG_LEVEL
: Controls logging verbosity (DEBUG, INFO, WARNING, ERROR)
The manimgl_wrapper.py
script can be customized to:
- Change default directories
- Modify history tracking behavior
- Add custom preprocessing steps
- Integrate with other tools
"ModuleNotFoundError: No module named 'manimlib'"
pip install manimgl
"Permission denied" on Windows
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
MCP not recognizing the server
- Restart Cursor after updating
mcp.json
- Check that the wrapper script path is absolute
- Verify Python path is correct
Animations not showing preview window
- ManimGL preview window should open automatically
- Use interactive keys:
d
,f
,z
to control playback - Press
ESC
orCmd+Q
to quit
Run with verbose output:
python manimgl_wrapper.py --help # Shows all available options
python manimgl_wrapper.py my_script.py MyScene --log-level DEBUG
This setup is designed to be:
- Extensible: Easy to add new features
- Maintainable: Clear separation of concerns
- Portable: Works across different projects
- Documented: Comprehensive usage examples
Feel free to customize the wrapper script for your specific needs!
This setup guide and associated scripts are provided as-is for educational and development purposes. ManimGL itself is governed by its own license terms.
🎬 Happy Animating! Create beautiful mathematical animations with the power of AI assistance and organized project management.