Skip to main content
Glama

DaVinci Resolve MCP

#!/usr/bin/env python3 """ DaVinci Resolve Media Operations """ import logging import os from typing import List, Dict, Any logger = logging.getLogger("davinci-resolve-mcp.media") def list_media_pool_clips(resolve) -> List[Dict[str, Any]]: """List all clips in the media pool of the current project.""" if resolve is None: return [{"error": "Not connected to DaVinci Resolve"}] project_manager = resolve.GetProjectManager() if not project_manager: return [{"error": "Failed to get Project Manager"}] current_project = project_manager.GetCurrentProject() if not current_project: return [{"error": "No project currently open"}] media_pool = current_project.GetMediaPool() if not media_pool: return [{"error": "Failed to get Media Pool"}] # Get the root folder and all its clips root_folder = media_pool.GetRootFolder() if not root_folder: return [{"error": "Failed to get Root Folder"}] clips = root_folder.GetClipList() # Format clip info clip_info = [] for clip in clips: if clip: clip_info.append({ "name": clip.GetName(), "type": clip.GetClipProperty()["Type"], "duration": clip.GetClipProperty()["Duration"], "fps": clip.GetClipProperty().get("FPS", "Unknown") }) return clip_info if clip_info else [{"info": "No clips found in the media pool"}] def import_media(resolve, file_path: str) -> str: """Import a media file into the current project's media pool.""" if resolve is None: return "Error: Not connected to DaVinci Resolve" # Validate file path if not file_path: return "Error: File path cannot be empty" if not os.path.exists(file_path): return f"Error: File '{file_path}' does not exist" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Import the media file # DaVinci Resolve API expects a list of file paths imported_media = media_pool.ImportMedia([file_path]) if imported_media and len(imported_media) > 0: return f"Successfully imported '{os.path.basename(file_path)}'" else: return f"Failed to import '{file_path}'. The file may be in an unsupported format." def create_bin(resolve, name: str) -> str: """Create a new bin/folder in the media pool.""" if resolve is None: return "Error: Not connected to DaVinci Resolve" if not name: return "Error: Bin name cannot be empty" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Get the root folder to add the bin to root_folder = media_pool.GetRootFolder() if not root_folder: return "Error: Failed to get Root Folder" # Check if bin already exists (by checking the subfolders) folders = root_folder.GetSubFolderList() for folder in folders: if folder.GetName() == name: return f"Error: Bin '{name}' already exists" # Create the bin new_bin = media_pool.AddSubFolder(root_folder, name) if new_bin: return f"Successfully created bin '{name}'" else: return f"Failed to create bin '{name}'" def list_bins(resolve) -> List[Dict[str, Any]]: """List all bins/folders in the media pool of the current project.""" if resolve is None: return [{"error": "Not connected to DaVinci Resolve"}] project_manager = resolve.GetProjectManager() if not project_manager: return [{"error": "Failed to get Project Manager"}] current_project = project_manager.GetCurrentProject() if not current_project: return [{"error": "No project currently open"}] media_pool = current_project.GetMediaPool() if not media_pool: return [{"error": "Failed to get Media Pool"}] # Get the root folder root_folder = media_pool.GetRootFolder() if not root_folder: return [{"error": "Failed to get Root Folder"}] # Get all subfolders (bins) from the root folder folders = root_folder.GetSubFolderList() # Format bin info bin_info = [] # Add root folder information bin_info.append({ "name": root_folder.GetName() or "Master", "is_root": True, "clip_count": len(root_folder.GetClipList()) }) # Add all subfolders (bins) for folder in folders: if folder: bin_info.append({ "name": folder.GetName(), "is_root": False, "clip_count": len(folder.GetClipList()) }) return bin_info if len(bin_info) > 1 else [{"info": "No bins found in the media pool"}] def get_bin_contents(resolve, bin_name: str) -> List[Dict[str, Any]]: """Get the contents of a specific bin/folder in the media pool. Args: resolve: The DaVinci Resolve instance bin_name: The name of the bin to get contents from. Use 'Master' for the root folder. """ if resolve is None: return [{"error": "Not connected to DaVinci Resolve"}] project_manager = resolve.GetProjectManager() if not project_manager: return [{"error": "Failed to get Project Manager"}] current_project = project_manager.GetCurrentProject() if not current_project: return [{"error": "No project currently open"}] media_pool = current_project.GetMediaPool() if not media_pool: return [{"error": "Failed to get Media Pool"}] # Get the root folder root_folder = media_pool.GetRootFolder() if not root_folder: return [{"error": "Failed to get Root Folder"}] # Check if we want the root folder (Master) if bin_name.lower() == "master" or bin_name == root_folder.GetName(): clips = root_folder.GetClipList() return format_clip_list(clips, "Master") # Otherwise search for the bin in subfolders folders = root_folder.GetSubFolderList() target_folder = None for folder in folders: if folder and folder.GetName() == bin_name: target_folder = folder break if not target_folder: return [{"error": f"Bin '{bin_name}' not found in Media Pool"}] # Get clips from the target folder clips = target_folder.GetClipList() return format_clip_list(clips, bin_name) def format_clip_list(clips, bin_name: str) -> List[Dict[str, Any]]: """Helper function to format clip info from a clip list.""" if not clips: return [{"info": f"No clips found in bin '{bin_name}'"}] clip_info = [] for clip in clips: if clip: properties = clip.GetClipProperty() clip_info.append({ "name": clip.GetName(), "type": properties.get("Type", "Unknown"), "duration": properties.get("Duration", "Unknown"), "fps": properties.get("FPS", "Unknown"), "resolution": f"{properties.get('Width', '?')}x{properties.get('Height', '?')}", "bin": bin_name }) return clip_info def list_timeline_clips(resolve) -> List[Dict[str, Any]]: """List all clips in the current timeline.""" if resolve is None: return [{"error": "Not connected to DaVinci Resolve"}] project_manager = resolve.GetProjectManager() if not project_manager: return [{"error": "Failed to get Project Manager"}] current_project = project_manager.GetCurrentProject() if not current_project: return [{"error": "No project currently open"}] current_timeline = current_project.GetCurrentTimeline() if not current_timeline: return [{"error": "No timeline currently active"}] # Get all video tracks video_tracks = current_timeline.GetTrackCount("video") clip_info = [] for track_index in range(1, video_tracks + 1): # Note: Track indices in Resolve API are 1-based clips = current_timeline.GetItemListInTrack("video", track_index) for clip in clips: if clip: clip_info.append({ "name": clip.GetName(), "track": f"V{track_index}", "start_frame": clip.GetStart(), "end_frame": clip.GetEnd(), "duration": clip.GetDuration() }) # Get audio tracks as well audio_tracks = current_timeline.GetTrackCount("audio") for track_index in range(1, audio_tracks + 1): clips = current_timeline.GetItemListInTrack("audio", track_index) for clip in clips: if clip: clip_info.append({ "name": clip.GetName(), "track": f"A{track_index}", "start_frame": clip.GetStart(), "end_frame": clip.GetEnd(), "duration": clip.GetDuration() }) return clip_info if clip_info else [{"info": "No clips found in the current timeline"}] def add_clip_to_timeline(resolve, clip_name: str, timeline_name: str = None) -> str: """Add a media pool clip to the timeline. Args: resolve: The DaVinci Resolve instance clip_name: Name of the clip in the media pool timeline_name: Optional timeline to target (uses current if not specified) """ if not resolve: return "Error: Not connected to DaVinci Resolve" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Get all clips in root folder root_folder = media_pool.GetRootFolder() clips = root_folder.GetClipList() target_clip = None for clip in clips: if clip.GetName() == clip_name: target_clip = clip break if not target_clip: return f"Error: Clip '{clip_name}' not found in Media Pool" # Get the target timeline timeline = None if timeline_name: # Switch to the specified timeline timeline_count = current_project.GetTimelineCount() for i in range(1, timeline_count + 1): t = current_project.GetTimelineByIndex(i) if t and t.GetName() == timeline_name: timeline = t current_project.SetCurrentTimeline(timeline) break if not timeline: return f"Error: Timeline '{timeline_name}' not found" else: # Use current timeline timeline = current_project.GetCurrentTimeline() if not timeline: return "Error: No timeline currently active" # Add clip to timeline # We need to use media_pool.AppendToTimeline() which expects a list of clips result = media_pool.AppendToTimeline([target_clip]) if result and len(result) > 0: return f"Successfully added clip '{clip_name}' to timeline" else: return f"Failed to add clip '{clip_name}' to timeline" def delete_media(resolve, clip_name: str) -> str: """Delete a media clip from the media pool by name. Args: resolve: The DaVinci Resolve instance clip_name: Name of the clip to delete Returns: String indicating success or failure with detailed error message """ if resolve is None: return "Error: Not connected to DaVinci Resolve" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Get all clips in root folder and all subfolders all_clips = [] target_clip = None # Get the root folder root_folder = media_pool.GetRootFolder() if not root_folder: return "Error: Failed to get Root Folder" # Get clips from root folder root_clips = root_folder.GetClipList() if root_clips: all_clips.extend(root_clips) # Get clips from subfolders folders = root_folder.GetSubFolderList() for folder in folders: if folder: folder_clips = folder.GetClipList() if folder_clips: all_clips.extend(folder_clips) # Find the clip by name for clip in all_clips: if clip and clip.GetName() == clip_name: target_clip = clip break if not target_clip: return f"Error: Clip '{clip_name}' not found in Media Pool" # Delete the clip try: result = media_pool.DeleteClips([target_clip]) if result: return f"Successfully deleted clip '{clip_name}' from Media Pool" else: return f"Failed to delete clip '{clip_name}' from Media Pool" except Exception as e: return f"Error deleting clip: {str(e)}" def move_media_to_bin(resolve, clip_name: str, bin_name: str) -> str: """Move a media clip to a specific bin in the media pool. Args: resolve: The DaVinci Resolve instance clip_name: Name of the clip to move bin_name: Name of the target bin Returns: String indicating success or failure with detailed error message """ if resolve is None: return "Error: Not connected to DaVinci Resolve" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Get the root folder root_folder = media_pool.GetRootFolder() if not root_folder: return "Error: Failed to get Root Folder" # Find the target bin target_folder = None # Check if we want the root folder if bin_name.lower() == "master" or bin_name == root_folder.GetName(): target_folder = root_folder else: # Search in subfolders folders = root_folder.GetSubFolderList() for folder in folders: if folder and folder.GetName() == bin_name: target_folder = folder break if not target_folder: return f"Error: Bin '{bin_name}' not found in Media Pool" # Find the clip by name all_clips = [] target_clip = None # Get clips from root folder root_clips = root_folder.GetClipList() if root_clips: all_clips.extend(root_clips) # Get clips from subfolders folders = root_folder.GetSubFolderList() for folder in folders: if folder: folder_clips = folder.GetClipList() if folder_clips: all_clips.extend(folder_clips) # Find the clip by name for clip in all_clips: if clip and clip.GetName() == clip_name: target_clip = clip break if not target_clip: return f"Error: Clip '{clip_name}' not found in Media Pool" # Move the clip to the target bin try: result = media_pool.MoveClips([target_clip], target_folder) if result: return f"Successfully moved clip '{clip_name}' to bin '{bin_name}'" else: return f"Failed to move clip '{clip_name}' to bin '{bin_name}'" except Exception as e: return f"Error moving clip: {str(e)}" def auto_sync_audio(resolve, clip_names: List[str], sync_method: str = "waveform", append_mode: bool = False, target_bin: str = None) -> str: """Sync audio between clips with customizable settings. Args: resolve: The DaVinci Resolve instance clip_names: List of clip names to sync sync_method: Method to use for synchronization - 'waveform' or 'timecode' append_mode: Whether to append the audio or replace it target_bin: Optional bin to move synchronized clips to Returns: String indicating success or failure with detailed error message """ if resolve is None: return "Error: Not connected to DaVinci Resolve" if not clip_names or len(clip_names) < 2: return "Error: At least two clips are required for audio synchronization" # Validate sync method if sync_method not in ["waveform", "timecode"]: return "Error: Sync method must be 'waveform' or 'timecode'" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Get all clips from media pool root_folder = media_pool.GetRootFolder() if not root_folder: return "Error: Failed to get Root Folder" all_clips = [] # Get clips from root folder root_clips = root_folder.GetClipList() if root_clips: all_clips.extend(root_clips) # Get clips from subfolders folders = root_folder.GetSubFolderList() for folder in folders: if folder: folder_clips = folder.GetClipList() if folder_clips: all_clips.extend(folder_clips) # Find clips by name clips_to_sync = [] for name in clip_names: found = False for clip in all_clips: if clip and clip.GetName() == name: clips_to_sync.append(clip) found = True break if not found: return f"Error: Clip '{name}' not found in Media Pool" # Set the clips as selected in media pool try: result = media_pool.SetCurrentFolder(root_folder) if not result: return "Error: Failed to set current folder to root" result = media_pool.SelectClips(clips_to_sync) if not result: return "Error: Failed to select clips for syncing" # Set sync options sync_options = { "syncMethod": 0 if sync_method == "waveform" else 1, # 0 for waveform, 1 for timecode "appendMode": append_mode # True to append audio, False to replace } # Perform the auto sync result = media_pool.AutoSyncAudio(sync_options) if not result: return "Error: Failed to sync audio for the selected clips" # If a target bin is specified, move the synced clips there if target_bin: target_folder = None # Check if we want the root folder if target_bin.lower() == "master" or target_bin == root_folder.GetName(): target_folder = root_folder else: # Search in subfolders for folder in folders: if folder and folder.GetName() == target_bin: target_folder = folder break if not target_folder: return f"Warning: Synced clips but bin '{target_bin}' not found for moving clips" # Move the synced clips to the target bin move_result = media_pool.MoveClips(clips_to_sync, target_folder) if not move_result: return f"Warning: Synced clips but failed to move them to bin '{target_bin}'" return f"Successfully synced audio for {len(clips_to_sync)} clips using {sync_method} method" except Exception as e: return f"Error syncing audio: {str(e)}" def unlink_clips(resolve, clip_names: List[str]) -> str: """Unlink specified clips, disconnecting them from their media files. Args: resolve: The DaVinci Resolve instance clip_names: List of clip names to unlink Returns: String indicating success or failure with detailed error message """ if resolve is None: return "Error: Not connected to DaVinci Resolve" if not clip_names or len(clip_names) == 0: return "Error: No clip names provided for unlinking" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Get all clips from media pool root_folder = media_pool.GetRootFolder() if not root_folder: return "Error: Failed to get Root Folder" all_clips = [] # Get clips from root folder root_clips = root_folder.GetClipList() if root_clips: all_clips.extend(root_clips) # Get clips from subfolders folders = root_folder.GetSubFolderList() for folder in folders: if folder: folder_clips = folder.GetClipList() if folder_clips: all_clips.extend(folder_clips) # Find clips by name clips_to_unlink = [] not_found_clips = [] for name in clip_names: found = False for clip in all_clips: if clip and clip.GetName() == name: clips_to_unlink.append(clip) found = True break if not found: not_found_clips.append(name) if not_found_clips: return f"Error: Clips not found in Media Pool: {', '.join(not_found_clips)}" if not clips_to_unlink: return "Error: No valid clips found to unlink" try: # Unlink the clips result = media_pool.UnlinkClips(clips_to_unlink) if result: return f"Successfully unlinked {len(clips_to_unlink)} clips" else: return "Error: Failed to unlink clips" except Exception as e: return f"Error unlinking clips: {str(e)}" def relink_clips(resolve, clip_names: List[str], media_paths: List[str] = None, folder_path: str = None, recursive: bool = False) -> str: """Relink specified clips to their media files. Args: resolve: The DaVinci Resolve instance clip_names: List of clip names to relink media_paths: Optional list of specific media file paths to use for relinking folder_path: Optional folder path to search for media files recursive: Whether to search the folder path recursively Returns: String indicating success or failure with detailed error message """ if resolve is None: return "Error: Not connected to DaVinci Resolve" if not clip_names or len(clip_names) == 0: return "Error: No clip names provided for relinking" if media_paths is None and folder_path is None: return "Error: Either media_paths or folder_path must be provided for relinking" if media_paths is not None and folder_path is not None: return "Error: Cannot specify both media_paths and folder_path, choose one approach" if media_paths is not None and len(media_paths) > 0 and len(media_paths) != len(clip_names): return "Error: If providing media_paths, the number must match the number of clip_names" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Get all clips from media pool root_folder = media_pool.GetRootFolder() if not root_folder: return "Error: Failed to get Root Folder" all_clips = [] # Get clips from root folder root_clips = root_folder.GetClipList() if root_clips: all_clips.extend(root_clips) # Get clips from subfolders folders = root_folder.GetSubFolderList() for folder in folders: if folder: folder_clips = folder.GetClipList() if folder_clips: all_clips.extend(folder_clips) # Find clips by name clips_to_relink = [] not_found_clips = [] for name in clip_names: found = False for clip in all_clips: if clip and clip.GetName() == name: clips_to_relink.append(clip) found = True break if not found: not_found_clips.append(name) if not_found_clips: return f"Error: Clips not found in Media Pool: {', '.join(not_found_clips)}" if not clips_to_relink: return "Error: No valid clips found to relink" try: # Relink the clips result = False if media_paths: # Relink with specific media paths # Note: The API expects clips and paths to be matched by index result = media_pool.RelinkClips(clips_to_relink, media_paths) else: # Relink by searching in folder path relink_options = {"recursive": recursive} result = media_pool.RelinkClips(clips_to_relink, [], folder_path, relink_options) if result: if media_paths: return f"Successfully relinked {len(clips_to_relink)} clips to specified media paths" else: return f"Successfully relinked {len(clips_to_relink)} clips using media from {folder_path}" else: if media_paths: return "Error: Failed to relink clips to specified media paths" else: return f"Error: Failed to relink clips using media from {folder_path}" except Exception as e: return f"Error relinking clips: {str(e)}" def create_sub_clip(resolve, clip_name: str, start_frame: int, end_frame: int, sub_clip_name: str = None, bin_name: str = None) -> str: """Create a subclip from the specified clip using in and out points. Args: resolve: The DaVinci Resolve instance clip_name: Name of the source clip start_frame: Start frame (in point) end_frame: End frame (out point) sub_clip_name: Optional name for the subclip (defaults to original name with '_subclip') bin_name: Optional bin to place the subclip in Returns: String indicating success or failure with detailed error message """ if resolve is None: return "Error: Not connected to DaVinci Resolve" if not clip_name: return "Error: Clip name cannot be empty" if start_frame >= end_frame: return "Error: Start frame must be less than end frame" if start_frame < 0: return "Error: Start frame cannot be negative" project_manager = resolve.GetProjectManager() if not project_manager: return "Error: Failed to get Project Manager" current_project = project_manager.GetCurrentProject() if not current_project: return "Error: No project currently open" media_pool = current_project.GetMediaPool() if not media_pool: return "Error: Failed to get Media Pool" # Get all clips from media pool root_folder = media_pool.GetRootFolder() if not root_folder: return "Error: Failed to get Root Folder" all_clips = [] # Get clips from root folder root_clips = root_folder.GetClipList() if root_clips: all_clips.extend(root_clips) # Get clips from subfolders folders = root_folder.GetSubFolderList() for folder in folders: if folder: folder_clips = folder.GetClipList() if folder_clips: all_clips.extend(folder_clips) # Find the source clip source_clip = None for clip in all_clips: if clip and clip.GetName() == clip_name: source_clip = clip break if not source_clip: return f"Error: Source clip '{clip_name}' not found in Media Pool" # Set the target folder for the subclip target_folder = root_folder if bin_name: target_folder = None # Check if it's the root folder if bin_name.lower() == "master" or bin_name == root_folder.GetName(): target_folder = root_folder else: # Search for the bin in subfolders for folder in folders: if folder and folder.GetName() == bin_name: target_folder = folder break if not target_folder: return f"Error: Target bin '{bin_name}' not found in Media Pool" # Set the current folder to the target folder result = media_pool.SetCurrentFolder(target_folder) if not result: return f"Error: Failed to set current folder to '{target_folder.GetName()}'" # Generate a subclip name if not provided if not sub_clip_name: sub_clip_name = f"{clip_name}_subclip" # Set in/out points for the source clip result = source_clip.SetMarkInOut(start_frame, end_frame) if not result: return f"Error: Failed to set in/out points on clip '{clip_name}'" try: # Create the subclip using CreateSubClip sub_clip = media_pool.CreateSubClip(sub_clip_name, source_clip) if sub_clip: # Clear the mark in/out points from the source clip source_clip.ClearMarkInOut() return f"Successfully created subclip '{sub_clip_name}' from frames {start_frame} to {end_frame}" else: # Clear the mark in/out points if subclip creation failed source_clip.ClearMarkInOut() return f"Error: Failed to create subclip from '{clip_name}'" except Exception as e: # Always clear the mark in/out points in case of exceptions try: source_clip.ClearMarkInOut() except: pass return f"Error creating subclip: {str(e)}"

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/samuelgursky/davinci-resolve-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server